skip to Main Content

I have trouble understanding why adding TEXT_NODE-s (document.createTextNode('some string')) into an existing parent element does not update the number of elements when using a for loop.

Don’t get me wrong, it makes modifying the the parent element easier, I just don’t understand why the length of array isn’t being updated.

I though that when working with this kind of elements, they are ‘alive’ ie. updating with changes made.

Edit #1:

Why isn’t const words = topNode.textContent.split(" "); being updated when new strings are added to the topNode-s element. ie: at the beggining the length is 2 (0 – one, 1 – two, 2 – three), when adding ‘some string’, I would expect the words to be length 4 (0 – one, 1 – two, 2 – some, 3 – string, 4 – three).

Edit #2:

I would like to know why the array length isn’t being updated, as I know that in some cases (I don’t know which cases…) the length of the array is being updated when changes are made. Why in some cases and not in this case?

Example:

const textarea = document.querySelector("#textarea");
const topNodes = textarea.childNodes;
for (const topNode of topNodes){
    if (topNode.nodeType === 3){
        let charCount = 0; //character count includes the number of letters and spaces - used for appending a word - function appendString
        const words = topNode.textContent.split(" ");
        for (let i = 0; i < words.length - 1; i++) {
            const word = words[i];
            if (typeof word !== 'undefined' && !word.length){
                charCount++; //in case of double space (currently irrelevant)
                continue;
            }
            charCount += word.length + 1; //adding + 1 as words are split by space
            if (word === 'two'){
                appendString(charCount, topNode, 'some string');
            }
            console.log("+-------------------------+");
            console.log("| current word count: ", words.length - 1, " |")
            console.log("|  actual word count: ", topNode.textContent.split(" ").length - 1, " |")
        }
    }
}

function appendString(charCount, thisParentNode, string){
    const nodeText = thisParentNode.textContent;
    const before = nodeText.slice(0, charCount);
    const after = nodeText.slice(charCount);

    thisParentNode.textContent = before;
    
    if (string.charAt(string.length - 1) !== " "){
      string = string + " ";
    }

    const textNodeAfter = document.createTextNode(after);
    const textNodeAppend = document.createTextNode(string);

    thisParentNode.parentNode.insertBefore(textNodeAppend, thisParentNode.nextSibling);
    thisParentNode.parentNode.insertBefore(textNodeAfter, textNodeAppend.nextSibling);
    
    //and normalize all the way up to textarea
    const textarea = document.querySelector("#textarea");
    normalizeAll(thisParentNode, textarea);

    function normalizeAll(thisNode, topNode){
        if (thisNode !== topNode){
            thisNode.normalize();
            normalizeAll(thisNode.parentNode, topNode);
        } else {
           thisNode.normalize();
        }
    } 
}
#body{
  height: 100%
}
#textarea {
  min-height: 30%;
  border: 1px solid black;
}
<html>
  <body>
    <div id="textarea" contenteditable>one two three</div>
  </body>
</html>

To further explain why this is actually an ‘issue’, take the example below. If I wish to append some strings to the element, I need a character count (so they are added at the correct location).

If we take the variable topNode – which is a TEXT_NODE element; and we wish to add multiple strings (textNodes) to the element, I would expect that adding a string after the first time (2cnd, 3rd, 4th etc.) would be added at an incorrect place.

Example:

one two three two four two five

  • add ‘something’ after string ‘two’ is found.
  • use charCount to determine where to split the current TEXT_NODE, so ‘something’ can be inserted at the correct place.

loop nr. 2

charCount should be -> 7 (one two)

charCount is actually -> 7

insert ‘something’


loop nr. 4

charCount should be -> 27 (one two something three two)

charCount is actually -> 17 (one two three two)

when inserting something using charCount 17, it inserts it in the correct place (ie. one two something three two something)

if we would be using charCount 27, it would insert it at the wrong location.

Why is using charCount 17 correct?
I would expect that using 17 when inserting a string, it would be inserted at the wrong place (ie: one two somethingsomething three two)

I would expect that using 27 would place it in the correct location, but it does not.

2

Answers


  1. Why adding TEXT_NODEs into an existing parent element does not update the number of elements? I thought that when working with this kind of elements, they are ‘alive’ ie. updating with changes made.

    They do.

    const topNodes = textarea.childNodes;
    

    is not an array but a live NodeList of the children of that element. When you add more nodes to textArea, the collection will reflect that.

    Why isn’t const words = topNode.textContent.split(" "); being updated when new strings are added to the element.

    Because it’s an array created from a string (topNode.textContent). Neither is the string "live" – it’s just a value -, nor does the split() method know how to build a "live array". This code runs once, and unless you update the array yourself, it will keep the state that it was initialised to.

    Login or Signup to reply.
  2. Why isn’t const words = topNode.textContent.split(" "); being updated when new strings are added to the topNode-s element.

    Because there’s no mechanism in place to do that, and never has been. (I don’t know of any language which does this. If it’s ever implemented, it would either have to be in a new language or a new explicit syntax just for this functionality, as such as thing would fundamentally break close to all existing code.)

    topNode may indeed be a reference to an element on the page. But words most certainly is not. It’s just an array of strings derived from information on that element. To get the updated data, you’d need to query that element again.

    To simplify the same operation, consider the following:

    let x = 2;
    let y = 3;
    let z = x + y;
    x = 3;
    

    After these four lines of code, what value do you expect z to have and why?

    z will equal 5, even though x has been modified. This is because the value of z is not a live reference to the variables used to calculate it. It’s the value resulting from that calculation. Updating the value means performing the calculation again.

    If you want to approximate this functionality, that’s on some level what functions are for:

    let x = 2;
    let y = 3;
    let z = () => x + y;
    x = 3;
    

    Now z doesn’t have a numeric value, its value is a function which can be invoked to produce the value you want. That function does have a "live" reference to the x and y variables.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search