0

I want to replace only the text of a given element and it should be it's own function.

For example, something like this:

<div id="thisDivIsGettingTargeted">
    <span>
        Edit me!
        <i class="fas fa-edit"></i>
    </span>
</div>

should be replaced to this:

<div id="thisDivIsGettingTargeted">
    <span>
        Oh yeah
        <i class="fas fa-edit"></i>
    </span>
</div>

It's not always the first child, so I can't use firstChild.

I really don't have any idea, how to achieve this. I have to search for the lowest element and have to change it's text, but in this example, it always finds the i tag.

That's the function I wrote.

const get_object = element => {
    if (!element.hasAttribute("data-liner-stop-search") && element.childElementCount === 1){
        return get_object(element.firstElementChild)
    }
    return element
};

Here's another example:

<span class="text-one-liner">
    <span class="name">
        Username
        <span data-tooltip="A tooltip">
            <i class="fas fa-crown"></i>
        </span>
    </span>
</span>

.text-one-liner is targeted and "Username" should be changed.

Myzel394
  • 1,155
  • 3
  • 16
  • 40
  • You can check [this question](https://stackoverflow.com/questions/6520192/how-to-get-the-text-node-of-an-element). But I also think that many of the answers there are outdated/JQuery, so I won't flag it as duplicate – Christian Vincenzo Traina Jan 01 '20 at 14:24

2 Answers2

1

You can do this by filtering on the nodeType property. Note that whitespace used to format the code also appears as child nodes. You might want to filter on those too as in my example below (node.nodeValue.trim() !== "").

document.querySelector('#thisDivIsGettingTargeted span').childNodes.forEach((node) => {
  if(node.nodeType === Node.TEXT_NODE && node.nodeValue.trim() !== "") {
    node.nodeValue = "new text";
  }
});
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<div id="thisDivIsGettingTargeted">
    <span>
        Edit me!
        <i class="fas fa-edit"></i>
    </span>
</div>
Mark Baijens
  • 13,028
  • 11
  • 47
  • 73
  • Thank you, but this is again a specifiec solution. I would like to have a function, where I pass my `#thisDivIsGettingTargeted ` as (for example) `element` and a new `text` and that function automaticayll searchs for this text node and replaces it. – Myzel394 Jan 01 '20 at 15:07
  • @Myzel394 You can easily wrap this logic in a function that does this for you. Maybe you only need to build in some logic to look for "grandchildren" and other descendant as well. – Mark Baijens Jan 01 '20 at 15:20
  • I thought I did. I tried it in my function I posted in my question but it didn't work. Do you have an idea? – Myzel394 Jan 01 '20 at 16:29
0

If (and only if) your function always returns the i tag element (as you said) and your text node is just before that i, try this

const get_object = element => {
    if (!element.hasAttribute("data-liner-stop-search") && element.childElementCount === 1){
        return get_object(element.firstElementChild)
    }
    return element.previousSibling;
};

Or you can loop through all the child elements of your targeted element (using .getElementsByTagName("*")) and check for the nodeType of the element. nodeType of a text node is 3. If there's only one text node among all the children, the following (generic) condition inside a loop will work

(updated as per @josh.trow's suggestion)

if (element[i].nodeType === 3 && !element[i].innerText.trim().length)
    return element[i]

You can find a reference for nodeType here.

Akshit Mehra
  • 747
  • 5
  • 17
  • Works only on this specifiec example, but I want a generic solution. The text could also be after a tag or between multiple tags. – Myzel394 Jan 01 '20 at 13:57
  • I suspect you will hit many false flags on this by virtue of the HTML whitespace counting as a text node (unless the content is compressed) - you will need to as well verify the content is not empty (`if (element[i].nodeType === 3 && element[i].innerText.trim().length)` or something like that – josh.trow Jan 01 '20 at 14:15
  • @josh.trow I edited the answer and added `!element[i].innerText.trim().length` as the second check – Akshit Mehra Jan 01 '20 at 14:21