3

Given

<div><span>first</span><span>second</span></div>

and

span + span::before {
  content: ', ';
}

I'd like to retrieve the rendered string 'first, second' in JavaScript but .textContent ignores the content of the pseudo-element as it's not part of the DOM. Is there a way to get the actual rendered text?

Example: https://jsfiddle.net/silverwind/nyr5kohj/2/

silverwind
  • 3,296
  • 29
  • 31
  • Does this answer your question? [Selecting and manipulating CSS pseudo-elements such as ::before and ::after using jQuery](https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin) – Madhawa Priyashantha Feb 17 '20 at 08:37
  • 2
    .. dunno about this dupe, it's dived quite deep in [that answer](https://stackoverflow.com/a/21709814/3702797) how to *read* that content, which is not that complex of a task: https://jsfiddle.net/2f5kg4th/ I therefore step down of any action when I see previous questions asking for the same were already closed as dupes of this one... – Kaiido Feb 17 '20 at 08:51
  • The dupe is unjustified imho, but it gave me the right hint of `getComputedStyle` and I came up with my own answer below. @Kaiido you example seems even better than mine, I'd accept it as an answer. – silverwind Feb 17 '20 at 13:05

2 Answers2

2

Got it working using getComputedStyle. It does not support ::after but that shouldn't be too hard to add.

let text = '';
for (const child of children(document.querySelector('div'))) {
  if (child.nodeType === 1) {
    const before = window.getComputedStyle(child, '::before').getPropertyValue('content');
    if (before && before !== 'none') {
      text += before.replace(/^['"]/, '').replace(/['"]$/, '');
    }
  } else if (child.nodeType === 3) {
    text += child.textContent;
  }
}

alert(text);

function children(node) {
  const ret = [];
  for (let i = 0; i < node.childNodes.length; i++) {
    const child = node.childNodes[i];
    ret.push(child, ...children(child));
  }
  return ret;
}
silverwind
  • 3,296
  • 29
  • 31
  • Umm, what happens if you have `::before {content:'none'}`? You shouldn't be checking for "magic" string values like that. – Doin Dec 04 '22 at 11:20
  • I'm not sure, but maybe such a value is return with the surrounding quotes and you can distinguish. – silverwind Jan 19 '23 at 22:37
  • Ah yes... my mistake: you're correct. If you actually set `::before {content:"foo"}`, then `getPropertyValue()` returns `"\"foo\""`, _including_ the quotes. (In FF at least it seems these are always *double*-quotes, even if you used single quotes in the source CSS). So checking for "none" is correct here. – Doin Jan 21 '23 at 07:19
-1

This doesn't get the rendered text but it imitates the behavior and produces the same string:

Step 1: Get the parent div

Step 2: Map through the children of the parent to get the text in an array

Step 3: Join the array to a string

<div id="parent"><span>first</span><span>second</span></div>

const parent = document.getElementById("parent");
const childTextArray = [...parent.childNodes].map(child => child.innerText);
// => ["first", "second"]
const theStringYouAskedFor = childTextArray.join(", ");
// => "first, second"
fippli
  • 1
  • 4