Home > Software design >  how to write a function that returns all elements with same attribute from domtree?
how to write a function that returns all elements with same attribute from domtree?

Time:08-16

I saw somewhere that this question was asked in a faang interview and I cannot come up with an optimized solution or find it anywhere. So the question basically wants us to write a function that receives an input like this:

Input: findAllEle('color', '#fff');

and produces an output like this:

Output: Array of elements matching this color

by going through the DOM tree! The solution probably is using a BFS or DFS but then the edge case is what if the color selector is white or #ffffff instead of #fff for which I think we should use a Trie!

Can anyone implement Trie solution using javascript for those edge cases (having multiple different selector)?

CodePudding user response:

I've managed to do it, but, although the trie works in my version, it's kinda useless because it seems getComputedStyle always returns the color's rgb representation (which is why I had to include it in the trie).

I tried to make it as generic as possible so that you can use this with other dfs problems

class Trie {
  value = ''
  children = []

  constructor(...keys) {
    const firstLetters = Array.from(new Set(keys.map(k => k[0])))
    this.children = firstLetters.map(l => {
      return {
                ...new Trie(...keys.filter(k => k.startsWith(l)).map(k => k.substring(1))),
                value: l,
            }
    })
  }
}

const whiteColorTrie = new Trie('white', '#fff', '#ffffff', 'rgb(255, 255, 255)')

const existsInTrie = (trie, search, initialValue = '') => {
    const { value } = trie

    const acc = initialValue   (value ?? '')

    if (!search.startsWith(acc)) return false

    if (search === acc && acc.length !== 0) return true

    return trie.children.some(node => existsInTrie(node, search, acc))
}

const dfs = (root, getChildNodes, predicate) => {
    const children = getChildNodes(root)
    const matches = []

    if (predicate(root)) matches.push(root)

    children.forEach(child =>
        matches.push(...dfs(child, getChildNodes, predicate))
    )

    return matches
}

const elements = dfs(
    document.body,
    node => [...node.children],
    node => existsInTrie(whiteColorTrie, getComputedStyle(node).color)
)

CodePudding user response:

I think I understand the goal to be a recursive search through the dom, starting at some given node, seeking all descendants who have a given color.

The catch is that both the color given to the function and the colors expressed in the dom can be in several formats, like a name, rgb, 3-digit hex, or 6-digit hex.

The catch is solved by "normalizing" colors to the value returned by getComputedStyle (see here). When comparing, compare normalized input and node colors.

// turn any color into a standard-formatted rgb string
const normalizeColor = color => {
  const d = document.createElement("div");
  d.style.color = color;
  document.body.appendChild(d)
  const result = window.getComputedStyle(d).color;
  d.remove();
  return result;
}

// recursive search for descendant nodes with color
function childrenWithColor(el, color) {
  let result = [];
  let targetColor = normalizeColor(color);
  let elColor = normalizeColor(el.style.getPropertyValue('color'));
  if (elColor === targetColor) result.push(el);

  for (let i = 0; i < el.children.length; i  ) {
    let child = el.children[i];
    result = [...result, ...childrenWithColor(child, color)];
  }
  return result;
}

// chose "red" for this example
let el = document.getElementById('parent')
let results = childrenWithColor(el, 'red');
// expect: grandchild-a, grandchild-b, child-b, grandchild-d
console.log('red ones:', results.map(el => el.id).join(', '));
<div id="parent">
  parent has no style at all

  <div id="child-a" style="border: 1px solid black;">
    child-a has a style that isn't a color

    <div id="grandchild-a" style="color: rgb(255,0,0);">
      grandchild-a has rgb red
    </div>

    <div id="grandchild-b" style="color: rgb(255, 0, 0);">
      grandchild-b has rgb red formatted with whitespace
    </div>
  </div>

  <div id="child-b" style="color: #f00;">
    child-b has single digit hex red

    <div id="grandchild-c" style="color: rgb(0,0,255);">
      grandchild-c isn't red
    </div>

    <div id="grandchild-d" style="color: #ff0000;">
      grandchild-d has double digit hex red
    </div>
  </div>
</div>

  • Related