1

User can, by pressing a button, select a particular topic of interest. When that happens, various divs will either become visible or invisible depending on whether that div has a link referring to that topic.

function GetPostsByTopic(topic) {
  var area = document.getElementById("postArea");
  var topicAreas = area.getElementsByClassName("topicArea");

  for (i = 0; i < topicAreas.length; i++) {
    var children = topicAreas[i].children;

    var topics = [];
    for (j = 0; j < children.length; j++) {
      topics.push(children[j].getAttribute("asp-route-name"));
      document.getElementById("firstTest").innerHTML = children[j].toString();
    }

    var b = topics.includes(topic);
    if (b == true) {
      var parentId = document.getElementById(topicAreas[i]).parentNode.id;
      document.getElementById(parent).style.display = 'block';
    } else {
      document.getElementById(parent).style.display = 'none';
    }
  }

}
<div class="topicBox">
  <button class="topicButton" onclick="GetPostsByTopic('Pets')">Pets</button>
  <button class="topicButton" onclick="GetPostsByTopic('Vacation')">Vacation</button>
</div>

<div id="postArea">
  <div class="post" id="post1">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Pets">Pets</a>
    </div>
  </div>
  <div class="post" id="post2">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Vacation">Vacation</a>
    </div>
  </div>
  <div class="post" id="post3">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Pets">Pets</a>
    </div>
  </div>
</div>

The trouble, as far as I can tell, begin early in the JS part. I can see that when a do var children=topicAreas[i].children, I get nothing.

DBS
  • 9,110
  • 4
  • 35
  • 53
Natasha Drost
  • 133
  • 1
  • 10
  • 1
    I don't believe your example is correct. At least, there is nothing in your HTML that has the id "firstTest", hence `document.getElementById("firstTest").innerHTML` is giving an error. – Steven W. Klassen Jul 14 '20 at 14:45
  • Also, you are passing `getElementById` an element, not an id here: `var parentId = document.getElementById(topicAreas[i]).parentNode.id;`. – Turnip Jul 14 '20 at 14:45
  • I would actually suggest using jQuery instead of Javascript for this, what you want to reach is technically a single line in jQuery. – golddragon007 Jul 14 '20 at 14:48
  • Or, why not use `let children=document.querySelectorAll(".topicArea > a")`? See: https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll – Nate G Jul 14 '20 at 14:51
  • About the getElementById("firstTest")-part: yeah, i should've removed that from the example. But in the source-code, where I have copied this from, this should'nt give an error. – Natasha Drost Jul 14 '20 at 15:09
  • @NateG the reason I don't want to do that is because I have to know if the topicArea div itself holds a valid anchor tag. Hence, i have to go that over. – Natasha Drost Jul 14 '20 at 15:16

2 Answers2

0

Children isn't the issue. When you run your code you get the error "Uncaught TypeError: Cannot set property 'innerHTML' of null". Looking at your code where you are using .innerHTML, we see that you are trying to reference an element that you don't have in this code:

document.getElementById("firstTest")

Now, after adding that, you still have some items that you should change.

  • asp-action and asp-route-name are invalid HTML. Are you using a framework that requires this syntax?
  • Don't use .getElementsByClassName().
  • Use .querySelectorAll() and Array.forEach() on the result for easier looping.
  • Don't use .innerHTML when you aren't working with HTML strings as there are security and performance implications to doing so. Avoid inline styles when you can. Using them causes duplication of code and code is harder to scale. Instead, use CSS classes and the .classList API.

It's not super clear exactly what is supposed to happen when clicking your buttons, but see the updated code below:

function GetPostsByTopic(topic) {
    var area = document.getElementById("postArea");
    // Don't use .getElementsByClassName() as it provides a live node list
    // and causes quite a performance hit, especially when used in loops.
    // Use .querySelectorAll() and then use .forEach() on the collection that
    // it returns to iterate over them.
    area.querySelectorAll(".topicArea").forEach(function(area){
        var topics = [];
        // No need for children, here. Again, use .querySelectorAll()
        area.querySelectorAll("*").forEach(function(child) {   
          topics.push(child.getAttribute("asp-route-name"));
          document.getElementById("firstTest").textContent = child.getAttribute("asp-route-name");
        });
       
        if (topics.indexOf(topic) > -1) {
          // Don't use inline styles if you can avoid it.
          // Instead use pre-made classes.
          area.classList.add("hidden");
        }
        else {
          area.classList.remove("hidden");
        }
    });
}
/* Use CSS classes when possible instead of inline styles */
.hidden { display:none; }
<div class="topicBox">
        <button class="topicButton" onclick="GetPostsByTopic('Pets')">Pets</button>
        <button class="topicButton" onclick="GetPostsByTopic('Vacation')">Vacation</button>
</div>
<div id="postArea">
        <div class="post" id="post1">     
            <div class="topicArea">
                <a asp-action="Topic" asp-route-name="Pets">Pets</a>
            </div>     
        </div>
        <div class="post" id="post2">
            <div class="topicArea">
                <a asp-action="Topic" asp-route-name="Vacation">Vacation</a>
            </div>
        </div>
        <div class="post" id="post3">
            <div class="topicArea">
                <a asp-action="Topic" asp-route-name="Pets">Pets</a>
            </div>
        </div>      
</div>

<div id="firstTest"></div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0

I hope this is what you're trying to do. Based on what button you click, respective div is displayed.

function GetPostsByTopic(topic) {
    var area = document.getElementById("postArea");
    var topicAreas = area.getElementsByClassName("topicArea");
    for (i = 0; i < topicAreas.length; i++) {
        var children = topicAreas[i].children;
        for (j = 0; j < children.length; j++) {
           var parentId = topicAreas[i].parentNode.id;
           if(children[j].getAttribute("asp-route-name") === topic){
            document.getElementById(parentId).style.display = 'block';
           }else{
            document.getElementById(parentId).style.display = 'none';
           }
        }
    }
}
<div class="topicBox">
  <button class="topicButton" onclick="GetPostsByTopic('Pets')">Pets</button>
  <button class="topicButton" onclick="GetPostsByTopic('Vacation')">Vacation</button>
</div>

<div id="postArea">
  <div class="post" id="post1">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Pets">Pets</a>
    </div>
  </div>
  <div class="post" id="post2">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Vacation">Vacation</a>
    </div>
  </div>
  <div class="post" id="post3">
    <div class="topicArea">
      <a asp-action="Topic" asp-route-name="Pets">Pets</a>
    </div>
  </div>
</div>
BEAGLE
  • 343
  • 3
  • 10