Skip to content
Advertisement

How can I select a specific html paragraph with a specific class from a javascript array?

HTML:

<div class="topic" id="topicName">
        <div class="flexer">
          <h1 class="topic-header">Topic Name</h1>
          <img class="img" src="image.png" id="x">
        </div>
        <p class="subtopic">1</p>
        <p class="subtopic">2</p>
</div>

JS:

const functionName = function(event) {
  let test = event.target.parentNode.parentNode.childNodes.querySelector('p');
 
  console.log(test);
}

I want to select the paragraph with class subtopic, when the image is selected. I have many divs like this, so I need to find a way of ensuring each image has opens a specific paragraph with class subtopic.

I know .childNodes gives me the following array: div, p, p. I can’t explain the intricacies, but having a way to simply select the paragraph inside this div would help. I know its long winded but essentially by doing all this, i’m taking a non-specific event handler for the image and creating a specific effect for a particular paragraph.

Advertisement

Answer

To query for children, use Element.querySelector() for one, or Element.querySelectorAll() for multiple. (You can use these functions on document as well!)
To query for a parent, use Element.closest().
Note that these functions require a String containing CSS Selectors to work.

In your case I suggest to delegate the click-handler from the topic-wrapper (see my example-code below) to the <img>-elements.
This is useful, because that means that we only need to add a single event-listener to an element, instead of adding multiple listener to multiple elements which would do the same, saving us both code and memory usage.

To delegate, just add the listener to a parent, and use evt.target as the event-target for further usage.

The click-handler below does basically this:

  1. If clicked element is not of .topic-image, return
  2. Remove .show from all .subtopics
  3. Add .show to .subtopics of the same parent .topic

To make it easier to identify if the clicked image is actually the image that opens the subtopics, we can give it a specific class to check for (I used .topic-image).

Sidenote
Due to accessibility reasons, I suggest to wrap the image in a button, and delegate the click-listener to the button instead of the image.
This is beneficial because only adding a click-listener to a by-default non-clickable element is only accessible to (i guess) sighted mouse-users. Keyboard users can’t focus the element, can’t “click” it with keypresses and screen-readers won’t even announce it as clickable.
To achieve what a button basically does by default is quite a handful of code, hence the suggestion.

var twrapper = document.querySelector('#topic-wrapper');
twrapper.addEventListener('click', openTopic);

function openTopic(evt) {
  if (!evt.target.classList.contains('topic-image')) // If not a '.topic-image', return
    return;
  
  // Get '.topic-image's '.topic'-element
  var topic = evt.target.closest('.topic');
  
  // Remove '.show' from all '.subtopic's
  for (var sub of twrapper.querySelectorAll('p.subtopic'))
    sub.classList.remove('show');
  
  // Add back '.show' to affiliated paragraphs
  for (var sub of topic.querySelectorAll('p.subtopic'))
    sub.classList.add('show');
}
/* Styling for example-purpose; ignore */
.flexer img {
  width: 2rem;
  height: 2rem;
  background: gray;
}

/* Hide by default, show on '.show' */
p.subtopic {
  display: none;
}
p.subtopic.show {
  display: revert;
}
<div id="topic-wrapper">
  <div class="topic">
    <div class="flexer">
      <h1 class="topic-header">Topic Name</h1>
      <img class="topic-image" src="image.png">
    </div>
    <p class="subtopic">1</p>
    <p class="subtopic">2</p>
  </div>
  <div class="topic">
    <div class="flexer">
      <h1 class="topic-header">Topic Name</h1>
      <img class="topic-image" src="image.png">
    </div>
    <p class="subtopic">1</p>
    <p class="subtopic">2</p>
  </div>
</div>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement