user8490
user8490

Reputation: 291

Can we Directly remove Nodes from a NodeList?

document.getElementsByTagName returned me a NodeList object.

I would like to remove some items (let's say I would like to remove the first item from the NodeList)

Is there a way to do it? (I'm aware that I could manually copy all the nodes into an array but I do not wish to do that if NodeList by itself already has functions for us to remove elements in it)

I am aware that removing items from a NodeList has No Effect on the display (and should not cause any browser-display-refresh or stuff like that, I simply do not wish that NodeList object to hold a referennce to that Node)

is there a way to do it? (Or am i forced to copy all the items in the NodeList into an array?)

Upvotes: 29

Views: 45520

Answers (4)

Anna L
Anna L

Reputation: 49

I had this issue too today. Here is my code solution with comments. The situation was I had to manipulate a rather inflexible form and amend the js that someone else wrote - so not all of this is mine.

Basically, I created a loop to dump all of the node list items into an empty new array. Then I simply used the array.pop() method to remove the item I did not want the next part of the code to target. Since all the elements (form labels in this case) had the same class, I had to use the class of the parent element (paragraphs) to eliminate the element I wanted to exclude.

Then I used a array.forEach() method on my new array and inserted the second part of the code, which I mainly did not write. The idea of that is to remove the labels on the form and have the text-content of the label e.g. "first name" appear as the placeholder text in the form field itself.

Please note the real form is way more complicated, I only included the bare minimum html to get it to work here..

   //get all the labels - this is a node list 
    var labels = document.querySelectorAll(".field-label");
    
    //create empty array and create a loop to dump all the labels inside
var labelsNotRadio = [];
var i = labels.length;
while (i--) {
    var label = labels.item(i);
  
    labelsNotRadio.push(label);
    
    //create condition where if the PARENT element of the label contains a different class, then this one is removed from the array using array.pop()

    if (label.parentElement.classList.contains("pd-radio"))
        {
            console.log("this one is different");
            labelsNotRadio.pop(label);
        }

    console.log(labelsNotRadio);
    }  

 //Now we can do array.forEach() on the NEW array, which now doesn't contain the specific label we wanted to remove 

   labelsNotRadio.forEach( (item) => {
    
   

      var text = item.textContent;

      item.parentElement.classList.contains("required") && (text += " ");
      var nextElement = item.nextElementSibling;
      if (nextElement) {
        if (nextElement.tagName == 'SELECT') {
          nextElement.options[0].text = text;
        } else {
          nextElement.setAttribute("placeholder", text);
        }
        item.parentElement.removeChild(item);
      }
    }
)
  
<form>
<p class="required">
<label class="field-label">First Name</label>
<input type="text">
</p>

<p class="required">
<label class="field-label">Last Name</label>
<input type="text">
</p>

<p class="required">
<label class="field-label">Email</label>
<input type="text">
</p>

<p class="required pd-radio">
<label class="field-label">We give your two choices here:</label>
<label class="inline">YES<input type="radio"></label>
<label class="inline">NO<input type="radio"></label>
</p>







</form>

Upvotes: 0

user9020568
user9020568

Reputation:

var MySet = { 0: {value: document.body, enumerable: true}, 1: {value: document.head, enumerable: true}, 2:{value: document.documentElement, enumerable: true}, length: {value: 3}};
var protoNodelist = document.createDocumentFragment().childNodes;
var MyNodelist = Object.create(protoNodelist, MySet);
MyNodelist; // Output: NodeList {0: body, 1: head, 2: html, length: 3}
removeNode(MyNodelist, 1); // Output: NodeList {0: body, 1: html, length: 2}
    function removeNode(NodeList, i){
        var newElementSet = {};
        var newElementSeti = 0;
        for(NodeListi = 0; NodeListi < NodeList.length; NodeListi++){
            if(NodeListi != i){
                newElementSet[newElementSeti] = {value: NodeList[NodeListi], enumerable: true};
                newElementSeti += 1;
            };
        };
        newElementSet['length'] = {value: newElementSeti};
        var nodelist = document.createDocumentFragment().childNodes;
        var newNodelist = Object.create(nodelist, newElementSet);
        newNodelist.__proto__ = newNodelist.__proto__.__proto__;
        return newNodelist;
    };

Upvotes: -2

wk9876
wk9876

Reputation: 137

Just have the same use case as you.

For ES6 you can do this:

const myNodeList = ele.childNodes;

const [head, ...tail] = myNodeList;

console.log('the first item: ', head);

console.log('the remaining items: ', tail);

JavaScript ES6— The Spread Syntax (…)

Upvotes: 13

Felix Kling
Felix Kling

Reputation: 816364

As you can see in the specification, there is no method to remove an element from the list.

It would not make sense anyway. This NodeList is live, meaning the DOM is searched again whenever you access a certain property, e.g. the length. From MDC:

(...) The returned list lis live, meaning that it updates itself with the DOM tree automatically. (...)

So you have to copy the nodes into an array.

You can do this quite easily though by using Array methods. E.g. to copy it and directly remove the first element:

var nodes = [].slice.call(elements, 1); 

The NodeList is an array-like object. Hence we can apply array functions to it, by using call [docs]. [].slice is just a shorthand to get a reference to the slice [docs] method.

Upvotes: 30

Related Questions