1

I want to write a function in JavaScript that parses through all visible text on a website and performs a simple function on them.

The following code example uses JQuery:

$("*").each(function() {
  var t = $(this).text();
  if (t != "") {
    t = t.toUpperCase();
  }
});

Note: I understand that there are ways to perform similar functions to the above example in CSS. My intended use is to convert all roman text into an alternate Unicode writing system for the purpose of writing system learning, but I want to understand the fundamentals first.

The above does not function as intended as it pulls significantly more of the front-end code than I'm looking for. I looking for a function that only returns instances of text in:

  • Form items (Buttons, input, etc...)
  • Links
  • General page body text

How could I modify the above function to return the results I'm looking for?

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
Ethan Hill
  • 478
  • 2
  • 10
  • 24

1 Answers1

1

First figure out which selectors match the text you want to include or exclude. For example, to match anything but header and footer, you can use the selector

*:not(header):not(footer)

This can be used while recursively iterating to identify which elements to enter into and modify. (You can do this with a TreeWalker to select the text nodes you need.) Or, you can use

button, a

to select all buttons and anchors, then select all their descendant text nodes.

You want to select text nodes only - if you simply reassign the .text() of an element, you'll replace all of its contents, including HTML elements - you'll be corrupting the HTML and removing event listeners, etc. This isn't desirable. Just reassign the text of the text nodes instead:

// https://stackoverflow.com/q/2579666
const getTextNodes = (parent) => {
  const walker = document.createTreeWalker(
    parent,
    NodeFilter.SHOW_TEXT,
    null,
    false
  );
  const textNodes = [];

  while (node = walker.nextNode()) {
    textNodes.push(node);
  }
  return textNodes;
}

for (const elm of document.querySelectorAll('button, a')) {
  const descendantTextNodes = getTextNodes(elm);
  for (const textNode of descendantTextNodes) {
    textNode.textContent = textNode.textContent.toUpperCase();
  }
}
for (const input of document.querySelectorAll('input[type="button"]')) {
  input.value = input.value.toUpperCase();
}
<div>div</div>
<button>button</button>
<a>
  anchor
  <span>span descendant of anchor</span>
</a>
<input type="button" value="input">

Note that because button-like inputs don't have descendant text nodes, if you want to change their text, you'll have to do it separately.

The above example doesn't depend on a big library like jQuery, but jQuery doesn't have any decent way of selecting text nodes anyway.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320