0

using javascript or jquery, how could I solve for a scenario where I need to first hide all elements with class .manifest and then add an element to the DOM with the class .element after the rest are hidden. This is obviously difficult just because of the nature of javascript - so even a workaround would be more than acceptable for me.

Basically I've tried

var rows = document.getElementsByClassName('manifest');
    for (var i = 0; i < rows.length; i++) {
      rows[i].style.display = 'none';
    }

and then to later in the same script append an element with class .manifest but this does not work, that new element is just also hidden. I understand why that is, and it makes sense. I've just been unable to reason through a way to make this work.

GXM100
  • 417
  • 6
  • 20
  • Why is this tagged jquery if you're using regular DOM properties? – Barmar Nov 17 '20 at 23:51
  • The new element won't be hidden automatically. This only adds the `display` property to the elements that had the class when the loop runs. Elements added later won't have the property. – Barmar Nov 17 '20 at 23:52
  • Is it possible you have CSS like `.manifest { display: none; }`? That applies to all elements with the class. – Barmar Nov 17 '20 at 23:53
  • `document.querySelectorAll(".manifest").forEach(function(item){item.style.display="none";})` followed by `document.querySelector(".element").style.display="block" || "inline"` (depending on type of element) – Scott Marcus Nov 17 '20 at 23:53
  • @ScottMarcus That's how to undo this for all elements. The question is about a new element, not one of the elements that he hid originally. – Barmar Nov 17 '20 at 23:56
  • @Barmar I've addressed that in my comment. – Scott Marcus Nov 18 '20 at 00:01
  • Now I don't understand the question. What does `.element` have to do with `.manifest`? – Barmar Nov 18 '20 at 00:04
  • 1
    Please post a [mcve]. You can use a [Stack Snippet](https://meta.stackoverflow.com/questions/358992/ive-been-told-to-create-a-runnable-example-with-stack-snippets-how-do-i-do) to make it executable. – Barmar Nov 18 '20 at 00:05
  • Why is it "obviously difficult" to add an element after the other elements are hidden? – Barmar Nov 18 '20 at 00:08

2 Answers2

0

Put all the .manifest elements in a collection and loop over them, hiding each as you iterate (use CSS classes rather than inline CSS to do this as a best practice). Then, just show the .element item and create the new manifest element when the loop is complete.

And, don't use .getElementsByClassName(), which can really hurt performance in general, but in this case is the source of your issue. .getElementsByClassName() returns a "live node list", which keeps track of all matching elements at the time you create it and as the DOM gets changed from that point forward. Because you are adding a new element later, but one that has the same class (manifest) as the live node list contains, it's part of that node list and if you loop over that list and hide all the elements, the new element will be part of it. As you can see from the code below, no matter how many times you click the button (and loop over the manifest class items), the newly created one won't go away because we're not using a live node list here - - querySelectorAll() returns a "static node list".

document.querySelector("button").addEventListener("click", function(event){

  // Get all the .manifest items and loop over them
  document.querySelectorAll(".manifest").forEach(function(item){
    item.classList.add("hidden"); // Hide the item
  });

  document.querySelector(".element").classList.remove("hidden"); // Show this item
  
  // Now append a new manifest element that will be shown
  let manifest = document.createElement("div");
  manifest.textContent = "Dynamically added manifest element";
  document.querySelector("div").appendChild(manifest);
});
.hidden { display:none; }
<div>
  <div class="manifest">Manifest Element</div>
  <div class="manifest">Manifest Element</div>
  <div class="manifest">Manifest Element</div>
  <div class="manifest">Manifest Element</div>
  <div class="manifest">Manifest Element</div> 
  <div class="element hidden">Elment Element</div>
</div>

<button>Go!</button>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0
  1. Find the ref node to append elements
  2. Hide all the manifest nodes
  3. append new node on inside the parent node
  4. append the manifest node

// get ref for appending
const container = document.getElementsByClassName('manifest')[0].parentNode;
// hide all elements with classname manifest
Array.from(document.getElementsByClassName('manifest')).forEach(x => x.style.display = 'none')
// add new node to the DOM
const node = document.createElement('div');
node.className = 'element';
node.textContent = 'new element'
container.appendChild(node);
// add manifest node to the DOM
const manifestNode = document.createElement('div');
manifestNode.className = 'manifest';
manifestNode.textContent = 'manifest'
container.appendChild(manifestNode);
<div class="parent">
  <div class="manifest">manifest</div>
  <div class="manifest">manifest</div>
  <div class="manifest">manifest</div>
</div>
EugenSunic
  • 13,162
  • 13
  • 64
  • 86
  • `document.getElementsByClassName('manifest')[0]` <--- [No, no, no!](https://stackoverflow.com/questions/54952088/how-to-modify-style-to-html-elements-styled-externally-with-css-using-js/54952474#54952474) Also see my answer for why your solution won't work if you were to run your code a second time. – Scott Marcus Nov 18 '20 at 00:24
  • Also `Array.from(document.getElementsByClassName('manifest')).forEach()` really isn't helpful. Just use `document.getElementsByClassName('manifest').forEach()` since the node list returned from `.getEelementsByClassName()` (even though you shouldn't use that) can be iterated with `.forEach()`. – Scott Marcus Nov 18 '20 at 00:31