Skip to content

Conversation

@pertrai1
Copy link
Owner

@pertrai1 pertrai1 commented Jan 21, 2026

Summary by CodeRabbit

  • New Features

    • Added a DOM manipulation problem solution that finds descendant elements by computed style property/value.
  • Documentation

    • Updated progress metrics from 6/75 to 7/75 completed.
    • Added documentation for the new DOM-style query example and usage.

✏️ Tip: You can customize this high-level summary in your review settings.

@pertrai1 pertrai1 requested a review from Copilot January 21, 2026 12:50
@pertrai1 pertrai1 added the code challenge this is a coding challenge label Jan 21, 2026
@github-actions
Copy link

@codex review

1 similar comment
@github-actions
Copy link

@codex review

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

Claude finished @pertrai1's task in 1m 35s —— View job


Code Review: getElementsByStyle ✅

Great work implementing this DOM manipulation problem! The solution is correct and demonstrates solid understanding of DOM traversal and the Computed Style API. Here's my detailed review:


1. Correctness ✅

Verdict: The solution correctly implements the requirements.

  • ✅ Searches only descendants (excludes the root element itself)
  • ✅ Uses getComputedStyle() to match rendered styles (not just inline styles)
  • ✅ Returns an array of Elements (not HTMLCollection)
  • ✅ Properly handles the DFS traversal pattern
  • ✅ Edge cases handled: empty trees, no matches, nested elements

Mental trace with example:

// Given DOM: <div><span style="font-size: 12px">Span</span><p style="font-size: 12px">Paragraph</p></div>
getElementsByStyle(doc.body, 'font-size', '12px')
// 1. Iterates through body.children (the div)
// 2. Recursively checks div's children (span, p)
// 3. getComputedStyle returns "12px" for both
// 4. Both match and are added to results
// Returns: [span, p] ✅

2. Browser API Usage 🌐

getComputedStyle() - Correct Choice

  • ✅ Uses window.getComputedStyle() which returns computed (rendered) styles
  • ✅ Correctly uses .getPropertyValue(property) for flexible property access
  • ⚠️ Performance Note: getComputedStyle() triggers browser reflow and is expensive. For large DOM trees (1000s of nodes), this could be slow, but it's the correct API for this problem's requirements.

DOM Traversal Pattern

  • ✅ Uses element.children which returns only Element nodes (not text nodes)
  • ✅ Recursive DFS pattern is appropriate for tree traversal
  • ✅ Type-safe: Element type ensures we're working with actual DOM elements

3. Time & Space Complexity

Time Complexity: O(n)

  • Where n = number of descendant elements in the tree
  • We visit each element exactly once
  • getComputedStyle() and getPropertyValue() are O(1) operations
  • Array push is amortized O(1)

Space Complexity: O(h + m)

  • h = height of the DOM tree (call stack depth for recursion)
  • m = number of matching elements (result array)
  • In worst case: O(n) if all elements match or tree is completely unbalanced

4. Code Quality

Strengths:

  • ✅ Clear variable names (elements, computedStyle)
  • ✅ Clean separation of concerns (outer loop + recursive helper)
  • ✅ Follows TypeScript best practices with proper typing
  • ✅ Functional and readable code structure

Minor Improvement:

  • Line 9-11: The if (el === null) check is unnecessary since TypeScript guarantees el: Element cannot be null, and element.children never contains null values. See inline comment for details.

5. Learning Summary 📚

Key Concepts

Pattern: Depth-First Search (DFS) Tree Traversal

  • This solution uses DFS with recursion to traverse the DOM tree
  • Alternative: Could use BFS with a queue, but DFS is more natural for recursive DOM traversal

Browser API Insight: Computed Styles vs. Inline Styles

  • getComputedStyle() returns the final rendered styles after CSS cascade
  • This is different from element.style which only returns inline styles
  • Key learning: Always use getComputedStyle() when you need actual rendered values

Why This Approach Works:

  1. Excludes root: We iterate element.children in the outer loop (line 23), never checking the element itself
  2. Recursive descent: Each child becomes a new root for its subtree
  3. Order preserved: DFS naturally preserves document order (top-to-bottom, left-to-right)

Alternative Approaches

Approach 1: Iterative DFS with Stack

function getElementsByStyle(element: Element, property: string, value: string): Element[] {
  const elements: Element[] = [];
  const stack = [...element.children];
  
  while (stack.length > 0) {
    const el = stack.pop()!;
    if (getComputedStyle(el).getPropertyValue(property) === value) {
      elements.push(el);
    }
    stack.push(...Array.from(el.children).reverse());
  }
  return elements;
}

Trade-offs:

  • ✅ No recursion (avoids call stack limits for very deep trees)
  • ❌ Slightly more complex code
  • ❌ Needs .reverse() to maintain document order

Approach 2: BFS with Queue

function getElementsByStyle(element: Element, property: string, value: string): Element[] {
  const elements: Element[] = [];
  const queue = [...element.children];
  
  while (queue.length > 0) {
    const el = queue.shift()!;
    if (getComputedStyle(el).getPropertyValue(property) === value) {
      elements.push(el);
    }
    queue.push(...el.children);
  }
  return elements;
}

Trade-offs:

  • ✅ Natural document order without reverse
  • .shift() is O(n) for arrays (less efficient)
  • ⚡ Same time complexity overall: O(n)

Related Problems for Practice

  1. querySelector Implementation (GFE) - Similar DOM traversal, but with CSS selector matching
  2. getElementsByClassName - Similar problem but matching class names instead of styles
  3. LeetCode 897 - Increasing Order Search Tree - Tree traversal pattern (DFS in-order)
  4. LeetCode 102 - Binary Tree Level Order Traversal - BFS pattern comparison

Key Takeaways

  1. DFS Recursion Pattern: Outer loop over immediate children + recursive helper is a clean pattern for "descendants only" traversal
  2. Browser Performance: getComputedStyle() is powerful but expensive - use judiciously in production
  3. Type Safety: TypeScript's Element type prevents null/undefined bugs common in vanilla JS
  4. CSS Property Format: Browser-returned values are normalized strings - be aware of format variations across browsers

Final Thoughts

This is a solid, production-ready solution for a coding interview. The recursive DFS approach is clean and correct. The minor improvements suggested (removing unnecessary null check, being aware of performance) are nice-to-haves, not critical issues.

Rating: ⭐⭐⭐⭐⭐ (5/5)

  • Correctness: ✅ Perfect
  • Code Quality: ✅ Excellent
  • Browser API Usage: ✅ Correct
  • Performance: ✅ Optimal for the requirements

Keep up the great work! 🚀


@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

1 similar comment
@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

🧮 Solution Analysis

PR: GFE getElementsByStyle Problem
Files Changed: greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts

📊 Code Quality Results

ESLint Analysis

  • Errors: 5
  • Warnings: 0

Issues by file:

  • /home/runner/work/coding-challenges/coding-challenges/greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts: 5 errors, 0 warnings

🔍 Complexity Analysis

getElementsByStyle

📋 Analysis Results:

🔍 Automated Complexity Analysis

⚠️ Disclaimer: This is an automated analysis that may not be 100% accurate.
Always verify the complexity analysis manually, especially for complex algorithms.
Dynamic Programming, recursive, and mathematical algorithms may need manual review.

File: greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts
Generated: 2026-01-21T14:15:49.006Z

Time Complexity

Estimated: O(1)
Description: Constant time - single operations
Confidence: 54.2%

⚠️ Low Confidence: Please manually verify this analysis.
💡 Tip: Add // @complexity: O(n) time, O(1) space to override automated detection.

Space Complexity

Estimated: O(1)
Reason: Constant extra space

Data Structures Used

  • Array: []
    • access: O(1)
    • search: O(n)
    • insertion: O(1) amortized
    • deletion: O(n)
  • Stack/Queue: .push(
    • push/pop: O(1)
    • shift/unshift: O(n) for arrays

Code Metrics

  • Lines of Code: 20
  • Cyclomatic Complexity: 4
  • Max Nesting Level: 3

Recommendations

  • VERIFICATION: ⚠️ Manual Review Needed: Low confidence score suggests manual verification is required

💡 Analysis Tips

  • Time Complexity: Focus on the dominant operation in loops
  • Space Complexity: Consider auxiliary data structures
  • Optimization: Look for opportunities to improve efficiency
  • Edge Cases: Ensure your solution handles empty inputs, null/undefined, edge conditions

📁 Analysis Files Created

Detailed analysis files have been automatically generated and committed to docs/analysis/ for future reference.

🎯 Next Steps

  1. Review any ESLint errors/warnings above
  2. Consider the complexity analysis - is this the optimal approach?
  3. Test with edge cases mentioned in the problem description
  4. Add comments explaining complex logic
  5. Check the docs/analysis/ directory for detailed complexity analysis files

Analysis generated for PR files only

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

✅ Test Results

Tests have been executed for this pull request.

📊 Coverage Summary

Metric Coverage
Statements 94.61%
Branches 89.26%
Functions 85.71%
Lines 94.68%

Check the workflow logs for detailed test output.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Added a new DOM utility getElementsByStyle (implementation and docs), updated gfe-75 progress from 6/75 to 7/75, and incremented the GreatFrontEnd solved count from 12 to 13 in the main README. tsconfig now includes the DOM lib.

Changes

Cohort / File(s) Summary
Top-level docs
README.md
Incremented GreatFrontEnd "Problems Solved" from 12 to 13.
Project status
greatfrontend/gfe-75/README.md
Updated status from 6 / 75 to 7 / 75; marked getElementsByStyle as completed under DOM Manipulation.
New problem + implementation
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md, greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts
Added README documenting getElementsByStyle(element, property, value) and implemented the function returning an array of descendant Elements whose computed style property equals the given value (depth-first traversal).
TypeScript config
tsconfig.json
Added "dom" to the global lib array (now ["ES2020","dom"]) to enable DOM typings.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰✨ I hopped through nodes with keen delight,
Seeking styles by day and by night.
From twelve to thirteen, the tally grows—
Leaves tremble where getElementsByStyle goes. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'GFE getElementsByStyle Problem' clearly and specifically identifies the main change: implementing the getElementsByStyle problem as part of the GreatFrontEnd (GFE) coding challenges.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gfe-1212026

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link


🤖 Gemini Code Review (Model: gemini-2.5-flash)

## Code Review: `getElementsByStyle`

This review focuses on the `getElementsByStyle` function, which is a GreatFrontEnd problem solution.

### 1. Pattern Identification

This solution primarily uses the **Tree Traversal** pattern, specifically a **Depth-First Search (DFS)**. The DOM (Document Object Model) is inherently a tree structure, and the problem requires visiting all descendant nodes to check a condition. DFS is a natural fit for exploring all paths deeply before backtracking.

### 2. Complexity Verification

*   **Time Complexity**: **O(N)**, where `N` is the total number of descendant elements in the DOM subtree rooted at `element`.
    *   The `traverseDOM` function visits each descendant element exactly once.
    *   For each element, `getComputedStyle(el)` and `getPropertyValue(property)` are called. In most practical scenarios and for DSA problem analysis, these DOM API calls are considered to take effectively constant time (amortized O(1)) for a single property lookup, especially after the browser has computed styles.
    *   Therefore, the total time complexity is proportional to the number of elements visited.
*   **Space Complexity**: **O(N + D)**, where `N` is the number of matching elements stored in the `elements` array, and `D` is the maximum depth of the DOM tree (due to the recursion call stack).
    *   The `elements` array stores all matching `Element` references. In the worst case, all `N` descendant elements might match, leading to `O(N)` space.
    *   The recursive `traverseDOM` calls consume space on the call stack. The maximum depth of the recursion corresponds to the deepest path in the DOM tree, leading to `O(D)` stack space.

### 3. Key Insight

The key insight for this problem is twofold:

1.  **DOM as a Tree**: Recognizing that the DOM is a tree structure and that finding elements satisfying a condition requires a systematic **tree traversal** (like DFS or BFS).
2.  **`getComputedStyle` for Rendered Styles**: The problem specifies finding elements "rendered by the browser using the specified style." This immediately points to using the `window.getComputedStyle()` API. Unlike `element.style` (which only reflects inline styles), `getComputedStyle` provides the final, computed values of all CSS properties for an element as they are actually rendered by the browser, taking into account stylesheets, inheritance, and browser defaults.

### 4. Edge Case Analysis

The solution handles several edge cases correctly:

*   **Empty `element` (no children)**: If the initial `element` has no children, the `for (const child of element.children)` loop will not execute, and an empty array `[]` will be returned, which is correct.
*   **`element` with no matching descendants**: The `elements` array will remain empty throughout the traversal, correctly returning `[]`.
*   **Deeply nested matching descendants**: The DFS approach correctly explores the entire subtree and finds elements at any depth.
*   **`property` or `value` not found**: If `getComputedStyle.getPropertyValue(property)` returns a different value (e.g., an empty string for an invalid property, or a default value), the comparison `=== value` will correctly fail.
*   **Excluding the root `element`**: The problem statement "only descendants of the element argument are searched, not the element itself" is correctly handled by starting the traversal from `element.children` rather than `element` itself.
*   **Case sensitivity**: The solution correctly uses kebab-case for property names (e.g., `'font-size'`) which is standard for `getPropertyValue`. Values are matched exactly, which is usually the expectation.

### 5. Learning Points

*   **Similar Problems**:
    *   Any problem involving traversing a tree-like data structure (e.g., finding the maximum depth of a binary tree, counting nodes, searching for a specific node).
    *   Implementing custom versions of `document.getElementsByTagName`, `getElementsByClassName`, or `querySelector` without relying on the built-in browser APIs.
    *   Graph traversal problems where the graph can be represented as an adjacency list/matrix (DFS/BFS are fundamental).
*   **Common Mistakes with this Pattern**:
    *   **Incorrectly handling the root**: Forgetting to exclude or include the root node based on problem requirements.
    *   **Performance with DOM APIs**: Misunderstanding the cost of DOM operations, especially `getComputedStyle`. While often treated as O(1) for DSA, in real-world performance-critical applications, it can trigger layout recalculations, which are expensive.
    *   **Confusing `element.style` with `getComputedStyle`**: `element.style` only reflects inline `style` attributes, not styles from stylesheets or inherited styles. `getComputedStyle` is essential for rendered styles.
    *   **Stack Overflow**: For extremely deep trees, recursive DFS can lead to a stack overflow. Using an iterative DFS with an explicit stack is a common way to mitigate this.
*   **Variations of this Problem**:
    *   Implement `getElementsByStyle` using a **Breadth-First Search (BFS)**, which would return elements level by level.
    *   Find the *first* element that matches the style, instead of all of them.
    *   Support multiple style properties/values (e.g., `getElementsByStyle(el, { 'font-size': '12px', 'color': 'red' })`).
    *   Implement `getElementsByStyle` with a custom filtering function instead of a direct property/value match.

### 6. Code Quality

The code is generally clean and readable.

*   **Redundant `null` check**: Inside `traverseDOM`, the check `if (el === null)` is unnecessary. `el` will always be an `Element` because it's passed from `el.children`, which only contains `Element` instances. You can safely remove this line.
*   **Variable Naming**: `element`, `property`, `value`, `elements`, `el`, `computedStyle`, `child` are all descriptive and appropriate.
*   **Code Structure**: The use of a nested helper function `traverseDOM` is a good pattern for recursive traversals, keeping the main function clean and managing the `elements` array in the outer scope.

**Proposed Improvement:**

```typescript
export function getElementsByStyle(
  element: Element,
  property: string,
  value: string
): Element[] {
  const elements: Element[] = [];

  // Helper function for recursive DFS traversal
  function traverseDOM(el: Element) {
    // No need for null check here, as `el` will always be an Element from `el.children`.

    const computedStyle = getComputedStyle(el);

    // Check if the computed style property matches the target value
    if (computedStyle.getPropertyValue(property) === value) {
      elements.push(el);
    }

    // Recursively traverse children
    for (const child of el.children) {
      traverseDOM(child);
    }
  }

  // Start traversal from the children of the given element,
  // as per the problem statement "only descendants...not the element itself."
  for (const child of element.children) {
    traverseDOM(child);
  }
  return elements;
}

7. Alternative Approaches

  1. Iterative DFS using an explicit Stack:

    • Approach: Instead of recursive calls, maintain a stack (e.g., an array) to store elements to visit. Initialize the stack with element.children. In a while loop, pop an element, process it, and push its children onto the stack.
    • Trade-offs: Avoids potential JavaScript call stack limits for extremely deep DOM trees. Can sometimes be slightly harder to read than a simple recursive solution for beginners, but offers more control. Time and space complexity remain similar (O(N) time, O(N + D) space where D is max stack size).
  2. Iterative BFS using an explicit Queue:

    • Approach: Similar to iterative DFS, but use a queue (e.g., an array with shift() and push()) to store elements. Initialize the queue with element.children. In a while loop, dequeue an element, process it, and enqueue its children.
    • Trade-offs: Returns elements in a "level-by-level" order, which might be desirable for some problems. Also avoids recursion depth limits. Time and space complexity are similar to DFS.
  3. Using built-in DOM traversal APIs (TreeWalker or NodeIterator):

    • Approach: The browser provides optimized APIs like document.createTreeWalker() or document.createNodeIterator() for efficient DOM traversal. You would create a TreeWalker starting from element, then iterate through its nodes, applying your style check.
    • Trade-offs: More idiomatic and potentially more performant as these are native implementations. However, for a DSA learning context, explicitly implementing the traversal algorithm (DFS/BFS) is often the goal to demonstrate understanding. Using these APIs would abstract away the core algorithmic challenge.

The current recursive DFS approach is a perfectly valid and common way to solve this problem, demonstrating a fundamental tree traversal algorithm.

@github-actions
Copy link


🤖 Gemini Code Review (Model: gemini-2.5-flash)

Here's a detailed review of your getElementsByStyle solution, focusing on educational insights and best practices for GreatFrontEnd problems.


1. Pattern Identification

This solution primarily uses a Depth-First Search (DFS) traversal pattern. It recursively explores the DOM tree by visiting a node, then recursively visiting all its children before backtracking. This is a common pattern for traversing tree-like data structures such as the DOM.

2. Complexity Verification

  • Time Complexity: O(V * C)
    • Verification: The stated time complexity is generally understood to be O(V) where V is the number of nodes in the subtree if getComputedStyle is considered O(1). However, getComputedStyle itself can be computationally intensive. In the worst case, calling getComputedStyle for an element can trigger a recalculation of styles and layout, which might be proportional to the number of styles applied to the element or even the size of the render tree.
    • Correct Complexity: Let V be the number of descendant elements in the element's subtree. Each of these V elements is visited exactly once. For each element, getComputedStyle is called, followed by getPropertyValue. If we denote the cost of getComputedStyle and getPropertyValue as C, then the total time complexity is O(V * C). In many competitive programming contexts, C is often simplified to O(1) for DOM operations, leading to O(V). For a browser environment, it's more nuanced and C can be non-trivial.
  • Space Complexity: O(D + M)
    • Verification: The stated space complexity needs refinement.
    • Correct Complexity:
      • Recursion Stack: The recursive DFS approach uses the call stack. In the worst case (a deeply nested, linear DOM tree), the depth of the recursion stack would be O(D), where D is the maximum depth of the element's subtree.
      • Result Array: The elements array stores all matching elements. In the worst case, every descendant element could match the style, leading to O(M) space, where M is the number of matching elements (which can be up to V).
      • Therefore, the total space complexity is O(D + M).

3. Key Insight

The key insight for this problem is the understanding that to find elements rendered by the browser with a specific style, you must use window.getComputedStyle(). Simply checking element.style.propertyName would only reflect inline styles directly set on the element and would ignore styles applied via external stylesheets, inherited styles, or user-agent stylesheets, which are crucial for determining how an element is actually rendered. getComputedStyle() provides the final, resolved values of all CSS properties for an element.

4. Edge Case Analysis

The current solution handles several implicit edge cases correctly, but here are some explicit ones to consider:

  • element has no children: The initial for (const child of element.children) loop will simply not execute, and an empty array will be returned, which is correct.
  • No elements match the style: The elements array will remain empty and be returned, which is correct.
  • All elements match the style: All descendant elements will be added to the elements array, which is correct.
  • Property does not exist: If property is not a valid CSS property, computedStyle.getPropertyValue(property) will return an empty string "". The comparison "" === value will likely be false, correctly not matching elements.
  • Empty value string: If value is "", it will correctly match elements whose computed style for property is an empty string (e.g., display: "").
  • Deep DOM tree: A very deep DOM tree could potentially lead to a stack overflow due to excessive recursion depth in JavaScript engines, although modern engines have increased stack limits. This is a common concern for recursive DFS.

5. Learning Points

  • Similar Problems:
    • Any DOM traversal problem (e.g., getElementById, getElementsByClassName from scratch).
    • Tree traversal problems (e.g., finding the height of a tree, counting nodes, finding specific nodes in a binary tree or N-ary tree).
    • Problems involving searching or filtering nodes in a hierarchical structure.
  • Common Mistakes with this Pattern:
    • Not handling the root/initial element correctly: The problem explicitly states "only descendants... not the element itself." Your solution correctly implements this by initiating traversal on element.children.
    • Infinite loops: In tree structures, especially with graphs, forgetting to track visited nodes can lead to infinite loops. Not an issue with strict DOM trees.
    • Performance overhead of getComputedStyle: Repeated calls to getComputedStyle can be expensive as they might trigger layout recalculations. While unavoidable here, it's a crucial consideration in performance-critical frontend applications.
    • Stack overflow: For extremely deep trees, recursive solutions can hit the maximum call stack size. Iterative solutions (using an explicit stack for DFS or a queue for BFS) avoid this.
  • Variations of this Problem:
    • Find elements by attribute (e.g., data-test-id).
    • Find elements by text content.
    • Implement a simplified querySelector or querySelectorAll that supports basic CSS selectors.
    • Find elements that satisfy multiple style properties (e.g., font-size: 12px AND color: red).
    • Find elements whose style contains a certain value (e.g., font-family contains "Arial").

6. Code Quality

  • Variable Naming: element, property, value, elements, computedStyle, child are all clear and descriptive. el for the recursive helper parameter is also conventional.
  • Code Structure: The use of a nested helper function traverseDOM is a good pattern for encapsulating the recursive logic, making the main function cleaner.
  • Readability: The code is very readable and easy to understand.
  • TypeScript Best Practices:
    • Type annotations (Element, string, Element[]) are correctly used, enhancing type safety.
    • The if (el === null) check inside traverseDOM is redundant. Since el is typed as Element, it can never be null at that point. TypeScript's type system already guarantees this. You can safely remove this line.
// Current:
function traverseDOM(el: Element) {
  if (el === null) { // This check is unnecessary because 'el' is typed as Element
    return;
  }
  // ... rest of the code
}

// Improved:
function traverseDOM(el: Element) {
  const computedStyle = getComputedStyle(el);
  // ... rest of the code
}

7. Alternative Approaches

  1. Iterative Depth-First Search (DFS) with an explicit stack:

    • Approach: Instead of recursion, use an array as a stack. Initialize the stack with the children of the input element. In a loop, pop an element, process it (check its style), and then push its children onto the stack.
    • Trade-offs:
      • Pros: Avoids potential stack overflow issues with deep DOM trees. Can sometimes be slightly faster due to less overhead from function calls.
      • Cons: Can be slightly more verbose than recursive DFS.
      • Example Structure:
        const stack: Element[] = Array.from(element.children);
        while (stack.length > 0) {
          const el = stack.pop()!; // Non-null assertion as we check length
          // ... check style ...
          for (let i = el.children.length - 1; i >= 0; i--) { // Push children in reverse order to maintain DFS order
            stack.push(el.children[i]);
          }
        }
  2. Breadth-First Search (BFS) with a queue:

    • Approach: Use an array as a queue. Initialize the queue with the children of the input element. In a loop, dequeue an element, process it, and then enqueue all its children. This explores the DOM level by level.
    • Trade-offs:
      • Pros: Also avoids stack overflow. Useful if you need to process elements layer by layer.
      • Cons: The order of elements in the result array will be different (level-by-level) compared to DFS.
      • Example Structure:
        const queue: Element[] = Array.from(element.children);
        let head = 0;
        while (head < queue.length) {
          const el = queue[head++];
          // ... check style ...
          for (const child of el.children) {
            queue.push(child);
          }
        }
  3. Using document.createTreeWalker (Advanced/Native API):

    • Approach: TreeWalker is a highly optimized native DOM API for traversing a subtree. You can create a TreeWalker and then iterate through its nodes. While it doesn't directly filter by computed style, you could use it to efficiently traverse and then apply your getComputedStyle check.
    • Trade-offs:
      • Pros: Extremely efficient for large DOM trees as it's implemented natively. Can define complex filtering logic.
      • Cons: More complex API to learn and use. Might be considered "overkill" for a basic problem unless performance is a primary constraint.

Overall, your solution is correct, clean, and effectively solves the problem using a standard DFS traversal. The feedback primarily aims to deepen your understanding of complexity, edge cases, and alternative patterns in a frontend context.

const elements: Element[] = [];

function traverseDOM(el: Element) {
if (el === null) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary null check: The el === null check is redundant here. The function is always called with valid Element objects from element.children, which cannot be null. TypeScript's type system already guarantees el: Element will never be null.

Suggested change
if (el === null) {
function traverseDOM(el: Element) {

This simplifies the code without affecting correctness.

return;
}

const computedStyle = getComputedStyle(el);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance consideration: getComputedStyle() is called for every element in the tree, even if the property doesn't match. While this is correct, be aware that getComputedStyle() triggers browser reflow and is relatively expensive.

For large DOM trees, consider:

  • Caching computed styles if querying multiple properties
  • Using element.style.getPropertyValue() first for inline styles (though this changes behavior)

For this problem's requirements (matching computed styles), the current approach is correct but worth noting the performance implications in real-world scenarios.


const computedStyle = getComputedStyle(el);

if (computedStyle.getPropertyValue(property) === value) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normalization consideration: Different browsers may return property values in different formats (e.g., "12px" vs "12.0px", or color values as "rgb(255, 0, 0)" vs "#ff0000"). The current strict equality check assumes the value parameter matches the browser's exact format.

For production code, you might want to normalize values, especially for:

  • Colors (rgb/hex/hsl conversions)
  • Dimensions (handling of units)
  • Whitespace differences

However, for this coding challenge, the strict equality is appropriate as the problem likely assumes normalized inputs.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new GreatFrontEnd problem solution for "getElementsByStyle", implementing a function that finds DOM elements matching a specified CSS property-value pair. The implementation uses a depth-first traversal approach to search through element descendants.

Changes:

  • Implements the getElementsByStyle function using computed styles and recursive DOM traversal
  • Adds problem documentation with description, examples, and constraints
  • Updates progress trackers showing 7/75 GFE problems and 13 total GreatFrontEnd problems completed

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
getElementsByStyle.ts New TypeScript implementation using recursive DFS to find elements with matching computed styles
getElementsByStyle/README.md Problem description explaining the API requirements and providing usage examples
gfe-75/README.md Updates GFE 75 progress from 6 to 7 problems, adds getElementsByStyle to DOM Manipulation section
README.md Updates GreatFrontEnd total count from 12 to 13 problems

Implement a method `getElementsByStyle()` that finds DOM elements that are rendered by the browser using the specified style. It is similar to `Element.getElementsByClassName()` but with some differences:

- It is a pure function which takes in an element, a property string, and a value string representing the style's property/value pair to be matched on the elements descendants. E.g. `getElementsByStyle(document.body, 'font-size', '12px')`.
- Similar to `Element.getElementsByClassName()``, only descendants of the element argument are searched, not the element itself.
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a double backtick here. It should be a single backtick to properly format "Element.getElementsByClassName()".

Suggested change
- Similar to `Element.getElementsByClassName()``, only descendants of the element argument are searched, not the element itself.
- Similar to `Element.getElementsByClassName()`, only descendants of the element argument are searched, not the element itself.

Copilot uses AI. Check for mistakes.
Comment on lines 9 to 12
if (el === null) {
return;
}

Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This null check is unnecessary because the Element type doesn't include null, and the function is only called with elements from el.children which are always valid Element objects. This check can be removed to simplify the code.

Suggested change
if (el === null) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 27
export function getElementsByStyle(
element: Element,
property: string,
value: string
): Element[] {
const elements: Element[] = [];

function traverseDOM(el: Element) {
if (el === null) {
return;
}

const computedStyle = getComputedStyle(el);

if (computedStyle.getPropertyValue(property) === value) {
elements.push(el);
}

for (const child of el.children) {
traverseDOM(child);
}
}
for (const child of element.children) {
traverseDOM(child);
}
return elements;
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This DOM manipulation function lacks test coverage. The repository pattern shows that GreatFrontEnd problems typically include comprehensive test files (e.g., debounce.test.ts, event-emitter.test.ts, flatten.test.ts). Consider adding a getElementsByStyle.test.ts file with test cases that cover various scenarios like matching elements, nested structures, and edge cases such as no matches or elements with inherited styles.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts`:
- Around line 1-5: The file getElementsByStyle uses DOM types like Element and
APIs like getComputedStyle but the TypeScript project lib only contains ES2020;
update the project's TypeScript configuration to include the DOM lib by adding
"dom" to the "lib" array in tsconfig.json (e.g., "lib": ["ES2020", "dom"]) so
the symbols used in getElementsByStyle are defined, or alternatively add a
triple-slash directive at the top of this file to reference the DOM lib if you
prefer a file-scoped fix.
🧹 Nitpick comments (2)
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md (1)

1-7: Add difficulty badge and link to GreatFrontEnd problem.

As per coding guidelines, problem READMEs should include a difficulty badge and link to the problem on the respective platform. Other problems in this repository follow this convention.

📝 Suggested addition
 # getElementsByStyle
+
+![Medium](https://img.shields.io/badge/Medium-orange)
+
+[View on GreatFrontEnd](https://www.greatfrontend.com/questions/javascript/get-elements-by-style)
 
 Implement a method `getElementsByStyle()` that finds DOM elements that are rendered by the browser using the specified style. It is similar to `Element.getElementsByClassName()` but with some differences:
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts (1)

8-11: Unnecessary null check.

The parameter el is typed as Element, and the function is only called with children from el.children, which yields Element objects (never null). This null check is unreachable.

♻️ Proposed simplification
   function traverseDOM(el: Element) {
-    if (el === null) {
-      return;
-    }
-
     const computedStyle = getComputedStyle(el);

@github-actions
Copy link

@codex review

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

Claude finished @pertrai1's task in 1m 38s —— View job


Code Review: getElementsByStyle ✅

Great work on this GreatFrontEnd solution! The implementation is correct and demonstrates solid understanding of DOM traversal and the Computed Style API. Here's my detailed educational review:


✅ 1. Correctness

Verdict: The solution correctly implements all requirements.

  • ✅ Searches only descendants (excludes the root element itself)
  • ✅ Uses getComputedStyle() to match rendered styles (not just inline styles)
  • ✅ Returns an array of Elements (not HTMLCollection)
  • ✅ Properly handles the DFS traversal pattern
  • ✅ Edge cases handled: empty trees, no matches, nested elements

Mental trace with the example:

// Given DOM: <div><span style="font-size: 12px">Span</span><p style="font-size: 12px">P</p></div>
getElementsByStyle(doc.body, 'font-size', '12px')

// Execution flow:
// 1. Start with body.children (the div)
// 2. traverseDOM(div): getComputedStyle(div).getPropertyValue('font-size') !== '12px' → skip
// 3. Recurse into div.children: [span, p]
// 4. traverseDOM(span): getComputedStyle(span).getPropertyValue('font-size') === '12px' → push span
// 5. traverseDOM(p): getComputedStyle(p).getPropertyValue('font-size') === '12px' → push p
// Returns: [span, p] ✅

🌐 2. Browser API Usage

getComputedStyle() - The Right Choice

  • ✅ Uses window.getComputedStyle() which returns computed (final rendered) styles
  • ✅ This includes styles from:
    • External stylesheets
    • Inline styles
    • Inherited styles
    • Browser defaults (user-agent styles)
  • ✅ Correctly uses .getPropertyValue(property) for flexible property access

Important distinction:

// ❌ Wrong approach - only checks inline styles:
if (el.style.fontSize === '12px') { ... }

// ✅ Correct approach - checks computed (rendered) styles:
if (getComputedStyle(el).getPropertyValue('font-size') === '12px') { ... }

DOM Traversal Pattern

  • ✅ Uses element.children which returns only Element nodes (not text/comment nodes)
  • ✅ Recursive DFS pattern is appropriate for tree traversal
  • ✅ Type-safe: Element type ensures we're working with actual DOM elements

⚠️ Performance Note: getComputedStyle() can trigger browser reflow and is expensive. For large DOM trees (1000+ nodes), this could be slow, but it's the correct API for this problem's requirements.


📊 3. Time & Space Complexity

Time Complexity: O(n)

  • Where n = total number of descendant elements in the subtree
  • We visit each element exactly once
  • getComputedStyle() and getPropertyValue() are considered O(1) per call*
  • Array .push() is amortized O(1)

*Note: In reality, getComputedStyle() can be expensive (browser-dependent), but for algorithmic analysis, we treat it as constant time.

Space Complexity: O(h + m)

  • h = height of the DOM tree (recursion call stack depth)
  • m = number of matching elements (result array)
  • Worst case: O(n) if all elements match or tree is completely linear (deep and unbalanced)

Example complexity scenarios:

Shallow wide tree (10 levels, 100 elements each):
- Time: O(1000) visits
- Space: O(10 + m) where m ≤ 1000

Deep narrow tree (100 levels, 2 elements each):
- Time: O(200) visits
- Space: O(100 + m) where m ≤ 200

💻 4. Code Quality

Strengths:

  • ✅ Clear, descriptive variable names (elements, computedStyle, child)
  • ✅ Clean separation: outer loop + recursive helper pattern
  • ✅ Proper TypeScript typing with Element[] return type
  • ✅ Functional and readable structure
  • ✅ Follows modern ES6+ conventions (const, for...of)

Minor Note on Original Code:
The original version had if (el === null) check on line 9-11, which was unnecessary since:

  • TypeScript guarantees el: Element cannot be null
  • element.children never contains null values
  • This has been cleaned up in the current version ✅

Additional Quality Points:

  • No mutation of input parameters ✅
  • No side effects ✅
  • Pure function design ✅

📚 5. Learning Summary

Key Concepts & Pattern

Pattern: Depth-First Search (DFS) Tree Traversal

  • This solution uses recursive DFS to traverse the DOM tree
  • Each node is visited before its children (pre-order traversal)
  • Document order is preserved (top-to-bottom, left-to-right)

Why DFS Works Here:

  1. Excludes root: We iterate element.children (line 19-21), never checking the element itself
  2. Recursive descent: Each child becomes a new root for its subtree
  3. Natural ordering: DFS maintains document order without extra bookkeeping

Browser API Insight: Computed Styles vs. Inline Styles

The key insight for this problem is understanding the difference:

API Returns Use Case
element.style.fontSize Only inline styles Modifying styles programmatically
getComputedStyle(element).getPropertyValue('font-size') Final rendered values Querying actual displayed styles

Example:

<style>.box { font-size: 16px; }</style>
<div class="box" id="myBox"></div>

<script>
const box = document.getElementById('myBox');

// ❌ Returns empty string (no inline style):
console.log(box.style.fontSize); // ""

// ✅ Returns computed value from stylesheet:
console.log(getComputedStyle(box).getPropertyValue('font-size')); // "16px"
</script>

Why This Matters: Always use getComputedStyle() when you need actual rendered values.


Alternative Approaches

Approach 1: Iterative DFS with Stack

function getElementsByStyle(element: Element, property: string, value: string): Element[] {
  const elements: Element[] = [];
  const stack: Element[] = [...element.children];
  
  while (stack.length > 0) {
    const el = stack.pop()!;
    
    if (getComputedStyle(el).getPropertyValue(property) === value) {
      elements.push(el);
    }
    
    // Push children in reverse order to maintain document order
    for (let i = el.children.length - 1; i >= 0; i--) {
      stack.push(el.children[i] as Element);
    }
  }
  
  return elements;
}

Trade-offs:

  • ✅ Avoids recursion (no call stack limits for very deep trees)
  • ✅ Safer for extremely deep DOM trees (1000+ levels deep)
  • ❌ Slightly more complex code
  • ❌ Needs reverse iteration to maintain document order
  • ⏱️ Same time complexity: O(n)

Approach 2: BFS with Queue

function getElementsByStyle(element: Element, property: string, value: string): Element[] {
  const elements: Element[] = [];
  const queue: Element[] = [...element.children];
  
  let index = 0;
  while (index < queue.length) {
    const el = queue[index++];
    
    if (getComputedStyle(el).getPropertyValue(property) === value) {
      elements.push(el);
    }
    
    queue.push(...Array.from(el.children));
  }
  
  return elements;
}

Trade-offs:

  • ✅ Natural document order without reverse iteration
  • ✅ Processes elements level-by-level (breadth-first)
  • ✅ Avoids recursion depth issues
  • ❌ Uses more memory (queue can be larger than stack)
  • ⏱️ Same time complexity: O(n)

Which to choose?

  • Recursive DFS (your solution): Clean, readable, standard for interviews ✅
  • Iterative DFS: Use if deep nesting is a concern (rare in practice)
  • BFS: Use if level-order traversal matters (not required here)

Related Problems for Practice

  1. GreatFrontEnd: querySelector Implementation

    • Similar DOM traversal, but with CSS selector matching
    • Builds on the same DFS/BFS patterns
    • Link: Search for "querySelector" on GreatFrontEnd
  2. GreatFrontEnd: getElementsByClassName

    • Almost identical problem, but matching class names
    • Good for reinforcing DOM traversal patterns
  3. LeetCode 897: Increasing Order Search Tree

    • Binary tree DFS (in-order traversal)
    • Demonstrates DFS pattern in a different context
    • Helps understand recursion patterns
  4. LeetCode 102: Binary Tree Level Order Traversal

    • BFS pattern comparison
    • Shows when BFS is more appropriate than DFS
  5. LeetCode 993: Cousins in Binary Tree

    • Requires tracking depth during traversal
    • Adds complexity to basic DFS pattern

Common Mistakes to Avoid

Mistake 1: Using element.style instead of getComputedStyle()

// Wrong - only checks inline styles:
if (el.style.fontSize === '12px') { ... }

Mistake 2: Including the root element

// Wrong - checks element itself:
traverseDOM(element); // Should start with element.children

Mistake 3: Returning HTMLCollection instead of Array

// Wrong return type:
return document.getElementsByClassName('...'); // HTMLCollection
// Correct:
return [...document.getElementsByClassName('...')]; // Array

Mistake 4: Not handling property name formats

// CSS properties use kebab-case in getPropertyValue:
getPropertyValue('font-size') // ✅ Correct
getPropertyValue('fontSize')  // ❌ Wrong

🎯 Key Takeaways

  1. DFS Recursion Pattern: The outer loop + recursive helper pattern is clean for "descendants only" traversal
  2. Browser API Understanding: getComputedStyle() is essential for querying rendered styles
  3. Type Safety: TypeScript's Element type prevents null/undefined bugs
  4. Performance Awareness: getComputedStyle() is expensive - use judiciously in production
  5. Property Name Format: Always use kebab-case with getPropertyValue()

Final Rating

Overall: ⭐⭐⭐⭐⭐ (5/5)

  • Correctness: Perfect implementation
  • Code Quality: Clean, readable, idiomatic TypeScript
  • Browser API Usage: Correct choice of getComputedStyle()
  • Performance: Optimal O(n) time complexity for the requirements
  • Best Practices: Follows modern JavaScript/TypeScript conventions

This is a production-ready solution that would pass any coding interview. Excellent work! 🚀


@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@github-actions
Copy link


🤖 Gemini Code Review (Model: gemini-2.5-flash)

Here's a detailed review of your getElementsByStyle solution:


1. Pattern Identification

This solution primarily uses a Tree Traversal pattern, specifically Depth-First Search (DFS). The DOM (Document Object Model) is a tree structure, and the problem requires visiting all descendant nodes to check a condition. The recursive traverseDOM function naturally implements a DFS approach.

2. Complexity Verification

  • Time Complexity: O(N)

    • Verification: The stated time complexity is accurate, assuming getComputedStyle and getPropertyValue are effectively constant time operations in practice for a single element.
    • Explanation: The algorithm visits each descendant Element node exactly once. For each visited node, it performs a getComputedStyle call and a property lookup. If N is the total number of descendant elements in the element's subtree, the traversal itself is O(N). While getComputedStyle can theoretically be more complex (e.g., O(S) where S is the number of style rules if it has to recalculate everything), in most practical browser implementations, for a single element, it's highly optimized and often considered amortized O(1). Therefore, the overall time complexity is dominated by visiting each node once, resulting in O(N).
  • Space Complexity: O(H + M)

    • Verification: The stated space complexity is accurate.
    • Explanation:
      • O(H) for recursion stack: The recursive traverseDOM function uses the call stack. In the worst case, if the DOM tree is very deep (e.g., a long chain of single child elements), the recursion depth can go up to H, the height of the tree.
      • O(M) for results array: The elements array stores all the matching Element nodes. In the worst case, all N descendant elements could match the criteria, leading to M = N elements stored.
      • Combining these, the total space complexity is O(H + M). Since M can be up to N (total elements) and H can be up to N (skewed tree), in the worst case, it simplifies to O(N).

3. Key Insight

The key insight for this problem has two main components:

  1. Using window.getComputedStyle(): The problem asks to find elements "rendered by the browser using the specified style." This is crucial because element.style.propertyName only reflects inline styles set directly on the element's style attribute. To get the actual, final computed and rendered style (which accounts for CSS stylesheets, inheritance, browser defaults, etc.), you must use window.getComputedStyle(element).
  2. Excluding the Root Element: The problem explicitly states, "only descendants of the element argument are searched, not the element itself." The solution correctly addresses this by initiating the traverseDOM function on each of element.children rather than on element itself. This ensures the initial element passed to getElementsByStyle is never checked or added to the results.

4. Edge Case Analysis

The current solution handles several common edge cases correctly:

  • Empty element (no children): If element.children is empty, the initial loop won't run, and an empty array [] will be returned, which is correct.
  • No matching elements: If no elements satisfy the style condition, the elements array will remain empty and be returned.
  • All descendant elements match: All matching elements will be collected in the elements array.
  • Invalid property or value: If property is not a valid CSS property or value doesn't match, getComputedStyle().getPropertyValue() will likely return an empty string or a default value, which will then correctly not match the value argument (unless value itself is an empty string or the default). This behavior is generally robust.

Potential Edge Cases to Consider (and how the solution handles them):

  • element is not an Element (e.g., document, document.body but passed in a context where Element is expected): The TypeScript signature element: Element enforces this type safety at compile time. If compiled to JS and an HTMLElement or Document is passed, getComputedStyle will still work correctly. document itself doesn't have a style attribute, but document.body does. The type Element is broad enough to cover various DOM elements.
  • element is a text node or comment node: Again, the Element type constraint prevents this. Text nodes don't have children or getComputedStyle in the same way.

The solution seems robust for the specified problem constraints.

5. Learning Points

  • Similar Problems using this pattern:

    • DOM Traversal: Any problem that requires visiting all nodes (or a subset) in the DOM tree, such as implementing a custom querySelector, finding all images, or iterating through all form elements.
    • General Tree Traversal: Many problems on LeetCode involving binary trees, N-ary trees, or graphs often use DFS or BFS. Examples include "Maximum Depth of Binary Tree", "Path Sum", "Number of Islands" (graph as a grid), or "Clone Graph".
    • Recursive Search/Filtering: Problems where you need to filter elements based on a condition applied to each node in a hierarchical structure.
  • Common Mistakes with this pattern:

    • Incorrectly using element.style: As highlighted in the key insight, a common mistake is to use element.style.propertyName which only reflects inline styles, not the computed, rendered style.
    • Missing base cases or stopping conditions in recursion: While not an issue here (DOM traversal naturally terminates), in general tree problems, forgetting a base case can lead to infinite recursion or incorrect results.
    • Not handling the "descendants only" requirement: Accidentally checking or including the root element itself when the problem specifies descendants.
    • Performance for very large trees: For extremely deep DOM trees, recursion could theoretically hit call stack limits (though browsers typically have very high limits). An iterative approach (using an explicit stack for DFS or a queue for BFS) can mitigate this.
  • Variations of this problem:

    • Find elements by multiple style properties: Modify the condition to check multiple property/value pairs (e.g., font-size: 12px AND color: red).
    • Find elements matching a CSS selector: Implement a simplified querySelectorAll that uses CSS selectors. This would be a more complex parsing problem.
    • Find elements up the DOM tree: Traverse ancestors instead of descendants (e.g., closest()).
    • Return the first matching element: Stop traversal as soon as the first match is found.
    • Iterative implementation: Implement using an explicit stack (for DFS) or queue (for BFS) instead of recursion.
    • Performance optimization: For very frequent calls on a large DOM, one might consider caching computed styles or using a MutationObserver to react to style changes rather than re-traversing the entire tree.

6. Code Quality

The code quality is good:

  • Variable Naming: element, property, value, elements, el, computedStyle, child are all clear, descriptive, and follow standard conventions.
  • Code Structure: The use of a nested traverseDOM function is a clean and idiomatic way to implement recursive tree traversal while keeping the outer function focused on the initial setup and result collection.
  • Readability: The code is very readable and easy to understand.
  • TypeScript: Correctly uses TypeScript types (Element, string, Element[]), which enhances maintainability and helps catch errors early. The tsconfig.json update to include "dom" in lib is essential for getComputedStyle to be recognized correctly by TypeScript.

Minor Improvement (Optional):

You could make traverseDOM a named export helper function if you ever wanted to reuse it, but for this specific problem, nesting it is perfectly fine and often preferred for encapsulation.

// No change needed, but just for illustration of a minor alternative structure:
// export function _traverseDOM(el: Element, property: string, value: string, elements: Element[]) {
//   const computedStyle = getComputedStyle(el);
//   if (computedStyle.getPropertyValue(property) === value) {
//     elements.push(el);
//   }
//   for (const child of el.children) {
//     _traverseDOM(child, property, value, elements);
//   }
// }

// export function getElementsByStyle(...) {
//   const elements: Element[] = [];
//   for (const child of element.children) {
//     _traverseDOM(child, property, value, elements);
//   }
//   return elements;
// }

The current nested function is perfectly fine and often preferred for encapsulation.

7. Alternative Approaches

  1. Iterative DFS (using an explicit stack):

    • Approach: Instead of recursion, use an array as a stack. Initialize the stack with the children of the input element. In a loop, pop an element from the stack, check its style, and then push its children onto the stack.
    • Trade-offs: Avoids potential (though rare in browsers) recursion depth limits, potentially slightly better performance due to avoiding function call overhead, but can be slightly more verbose than recursive DFS.
  2. Iterative BFS (using an explicit queue):

    • Approach: Use an array as a queue. Initialize the queue with the children of the input element. In a loop, dequeue an element, check its style, and then enqueue its children.
    • Trade-offs: Finds elements level by level. For this specific problem, the choice between BFS and DFS doesn't significantly impact correctness or asymptotic complexity, but it changes the order in which matching elements are found (and thus, the order in the returned array). For problems like finding the shortest path, BFS is usually preferred.

Both iterative approaches offer similar time and space complexity to the recursive DFS for this problem, mainly differing in their traversal order and memory usage patterns (stack vs. queue). Your current recursive DFS is a perfectly valid and idiomatic solution.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md`:
- Around line 1-8: The README for getElementsByStyle is missing metadata and
complexity details; update
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md to include
the difficulty badge, a link to the original problem, constraints/expected
behavior summary, and a Big-O complexity section (time and space) for the
getElementsByStyle(element, property, value) function so it matches the repo
README guidelines and other problem READMEs; ensure you reference the function
name getElementsByStyle and describe that it searches only descendants (not the
element itself) when explaining constraints.
♻️ Duplicate comments (1)
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/getElementsByStyle.ts (1)

1-11: Add browser environment configuration for DOM API usage.

The ESLint configuration has "node": true but not "browser": true, causing no-undef errors for browser globals like Element and getComputedStyle(). Add an override in .eslintrc.json for greatfrontend/** files to enable the browser environment, rather than adding in-file comments. This aligns with the repo's existing pattern of directory-based overrides:

{
  "files": ["greatfrontend/**/*.{js,ts}"],
  "env": {
    "browser": true
  }
}

This should be added to the overrides array in .eslintrc.json alongside the existing leetcode/** and scripts/** overrides.

Comment on lines +1 to +8
# getElementsByStyle

Implement a method `getElementsByStyle()` that finds DOM elements that are rendered by the browser using the specified style. It is similar to `Element.getElementsByClassName()` but with some differences:

- It is a pure function which takes in an element, a property string, and a value string representing the style's property/value pair to be matched on the elements descendants. E.g. `getElementsByStyle(document.body, 'font-size', '12px')`.
- Similar to `Element.getElementsByClassName()`, only descendants of the element argument are searched, not the element itself.
- Return an array of `Elements`, instead of an `HTMLCollection` of `Elements`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add problem metadata + complexity section.

This README is missing the difficulty badge, problem link, constraints, and Big‑O complexity note. Please add them to align with the repo’s README guidelines. As per coding guidelines, ...

🤖 Prompt for AI Agents
In `@greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md` around
lines 1 - 8, The README for getElementsByStyle is missing metadata and
complexity details; update
greatfrontend/gfe-75/dom-manipulation/getElementsByStyle/README.md to include
the difficulty badge, a link to the original problem, constraints/expected
behavior summary, and a Big-O complexity section (time and space) for the
getElementsByStyle(element, property, value) function so it matches the repo
README guidelines and other problem READMEs; ensure you reference the function
name getElementsByStyle and describe that it searches only descendants (not the
element itself) when explaining constraints.

@pertrai1 pertrai1 merged commit d97cc1e into main Jan 22, 2026
7 checks passed
@pertrai1 pertrai1 deleted the gfe-1212026 branch January 22, 2026 13:38
@github-actions
Copy link

📅 Spaced Repetition Reviews Scheduled!

Great job solving #GFE-697b - GetElementsByStyle! 🎉

To help you retain this knowledge long-term, I've scheduled 5 review sessions using spaced repetition:

Review Interval Schedule Logic
1st Review 1 day after solving Scheduled now
2nd Review 3 days after 1st review Auto-scheduled when 1st completes
3rd Review 7 days after 2nd review Auto-scheduled when 2nd completes
4th Review 14 days after 3rd review Auto-scheduled when 3rd completes
5th Review 30 days after 4th review Auto-scheduled when 4th completes

What to expect:

  • Your 1st review is scheduled for tomorrow
  • Each subsequent review is scheduled automatically when you complete the previous one
  • This ensures proper spacing even if you complete a review a few days late
  • GitHub issues will be created automatically for each review
  • Each issue will link back to your solution

🧠 Why Spaced Repetition?

Research shows that reviewing material at increasing intervals dramatically improves retention. This adaptive scheduling ensures optimal spacing based on when you actually complete each review!

Check docs/reviews/review-schedule.json to see your review schedule.

github-actions bot pushed a commit that referenced this pull request Jan 22, 2026
  Problem: #GFE-697b - GetElementsByStyle
  PR: #208
  First review scheduled (subsequent reviews auto-scheduled on completion)

  [skip ci]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code challenge this is a coding challenge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants