0

I would like to select all elements that contain a specified text in a SVG tag.

Something like this

[...document.querySelectorAll("*")].filter(e => e.childNodes && [...e.childNodes].find(n => n.nodeValue?.match("something")))

Or an other response of this question

How can I select this text element by its containing text?

<body>
  <svg>
    <g>
      <text>something</text>
    </g>
  </svg>
</body>
Hunam
  • 69
  • 1
  • 10

1 Answers1

2

The [...document.querySelectorAll("*")] approach needs to parse the nodeValue of every element.
All the work is done by the (slower) JavaScript Engine.

Instead, you can use the TreeWalker API to go look for TextNodes only.
Now the work is done by the (faster) Browser Engine.

IE9 was the last Browser to implement the TreeWalker API, so this solution has been around for at least 10 years now...
(but for some reason people still prefer the slow jQuery contains or a-like solutions)

TreeWalker API

  • #Text Nodes are nodeType 4
    and remember whitespace (space,return,tab) between Nodes are TextNodes

  • Be aware nodeNames in the HTML NameSpace are UPPERCASE;
    but nodeNames in de SVG NameSpace are lowercase

<body>
  <svg something>
    <g id=somethingelse>
      <text id=ONE>something</text>
      <text id=TWO>nothing</text>
    </g>
  </svg>
</body>
<script>
  function findNodes(
    str = '', 
    root = document.body
  ) {
    let tree = document.createTreeWalker(root, 4);
    let nodes = [];
    let node;
    while (node = tree.nextNode()) {
      if (node.parentNode.nodeName === "text" 
          && 
          node.data.includes(str)) {
        nodes.push(node);
      }
    }
    console.log('Find:', str)
    nodes.map((text, idx, arr) => {
      console.log(`${idx+1}/${arr.length} `, text.data, ' in:', text.parentNode.id);
    });
    return nodes;
  }
  findNodes('something');
  findNodes('thing');
</script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49