taoqf / node-html-parser

A very fast HTML parser, generating a simplified DOM, with basic element query support.
MIT License
1.12k stars 112 forks source link

set textContent doesn't work as expected #169

Closed SergeiReutov closed 3 years ago

SergeiReutov commented 3 years ago

I'm using version 5.0.0

I assume textContent should work as getter \ setter, but apparently, it's not:

const root = HTMLParser.parse(`
  <div id="list">
    <span>
      Hello World
    </span>
  </div>
`);

const element = root.querySelector('#list');
console.log(element.textContent); // Hello World
element.textContent = 'Goodbye World';
console.log(element.toString()); // <div id="list">Goodbye World</div>

So it does return the inner text content, but instead of setting it, it replaces the innerHTML (it replaced the <span> tag in the example above).

Is there a way to replace just a text?

SergeiReutov commented 3 years ago

Just in case anyone else faced the same issue, here is my workaround:

const root = HTMLParser.parse(`
  <div id="list">
    <span>
      Hello World
    </span>
  </div>
`);

const element = root.querySelector('#list');
const textToReplace = element.textContent.trim();
const updatedInnerHTML = element.innerHTML.replace(
  textToReplace,
  'Goodbye World'
);
element.innerHTML = updatedInnerHTML;
console.log(element.toString()); // <div id="list"><span>Goodbye World</span></div>
nonara commented 3 years ago

Thanks for the report!

According to MDN spec (https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)

Both textContent and innerText remove child nodes when altered

The behaviour works as expected. Your code selects the div, so by setting textContent, you'd be wiping out the span.

That aside, you're trying to alter the text of an HTMLElement node, as opposed to directly changing the text of it's child TextNode.

You can see what the nodes look like by using: https://astexplorer.net/

The following should work:


const root = HTMLParser.parse(`
  <div id="list">
    <span>
      Hello World
    </span>
  </div>
`);

const rootEl = root.querySelector('#list');
const textEl = root.querySelector('#list > span').firstChild; // Points to: span > TextNode within the span
textEl.textContent = 'Goodbye World';
console.log(rootEl .toString());

Hope that helps!