6

Is it possible in JS to select the first element whatsoever with document.querySelector solely by a given textDocument, if the that textDocument is available on viewport when the code is executed?


I am looking for a way without the following code because it brings out all relevant tagNames and filters them via textContent, but I want to select them by text content, not to filter.

document.querySelectorAll('tagName').forEach( (e)=>{
    if (e.textContent.includes('Delete')) {
        e.click();
    }
});
user8551674
  • 173
  • 1
  • 2
  • 13
  • Are you just wanting to write shorter code, like `$("tagName:contains(Delete)").click()` in jQuery, or are you concerned about performance when filtering by content yourself? (I would think even if `.querySelectorAll()` directly supported this behind the scenes it would still need to iterate over all the elements of the specified type.) – nnnnnn Sep 07 '17 at 02:57
  • Hmm, I just want JS to make a search like I do with `CTRL+F` and use the first element with the exact `textContent` it supplied, and then work with it... If it is being done, I couldn't care less from code longevity or performance. :) – user8551674 Sep 07 '17 at 03:10

2 Answers2

5

There is no CSS selector targeting on textContent.

Also, as your code is currently written, it's quite easy to grab the first element which textContent includes this string, it will always be document.documentElement or null.

You should make your query a bit more strict.

You could probably build an XPath query to this very extent, but that would end up slower than iterating over all the nodes by yourself.

So if performance is an issue, a TreeWalker is the way to go.

Here is a function that will grab elements by textContent.
It has different optional arguments which will allow you to tell

  • if the query should be strict ("string === textContent" which is the default),
  • a node to start the search from (defaults to document.documentElement)
  • if you are only interested in the elements without children

function getElementByTextContent(str, partial, parentNode, onlyLast) {
  var filter = function(elem) {
    var isLast = onlyLast ? !elem.children.length : true;
    var contains = partial ? elem.textContent.indexOf(str) > -1 :
      elem.textContent === str;
    if (isLast && contains)
      return NodeFilter.FILTER_ACCEPT;
  };
  filter.acceptNode = filter; // for IE
  var treeWalker = document.createTreeWalker(
    parentNode || document.documentElement,
    NodeFilter.SHOW_ELEMENT, {
      acceptNode: filter
    },
    false
  );
  var nodeList = [];
  while (treeWalker.nextNode()) nodeList.push(treeWalker.currentNode);
  return nodeList;
}
// only the elements whose textContent is exactly the string
console.log('strict', getElementByTextContent('This should be found'))
// all elements whose textContent contain the string (your code)
console.log('partial', getElementByTextContent('This should', true))
// only the elements whose textContent is exactly the string and which are the last Element of the tree
console.log('strict onlyLast', getElementByTextContent('This should be found', false, null, true))
<p><span>This should be found</span></p>
<span>This should only in partial mode</span><br>
<span>This must not be found</span>
<!-- p should not be found in onlyLast mode -->
Kaiido
  • 123,334
  • 13
  • 219
  • 285
0

No, there is not. document.querySelector can only accept a string argument that describes one or more CSS selectors separated by commas. You cannot provide document.querySelector a textDocument.

You will have to check the content of a node different, with one way being the way you've described in the question.

Govind Rai
  • 14,406
  • 9
  • 72
  • 83
  • Same answer. You cannot select them by text content. Your current approach is the right approach. :) I wish it could be shorter, but as @nnnnnn states _I would think even if .querySelectorAll() directly supported this it would still need to iterate over all the elements behind the scenes._ – Govind Rai Sep 07 '17 at 02:59