Home > Mobile >  Using one function for multiple html classes in javascript (expanding text via read more button)
Using one function for multiple html classes in javascript (expanding text via read more button)

Time:09-29

First post on stack overflow:)

I'm trying to put one certain function on all of my HTML-buttons, so far I haven't found anything that worked after 2 days of trying out(new to coding).

What I'm basically trying to do is a 'read more' button that displays a text on click. Neither .getElementsByClassName nor .querySelectorAll have worked so far. If I use my code with just an #id it works fine, but I know there must be a better way of using one function for multiple elements instead of having multiple ids and using the function for each id individually . I've also tried a forEach() loop but it didn't do anything. Additionally I've tried readMoreBtn[length].addEventListener('click', () => {}), but that also didn't work.

As I said, it works with the .getElementById and querySelector, but not with .getElementsByClassName or .querySelectorAll. If I use querySelectorAll, the first button works but the others don't.

Since I'm interested in making it work with classes and not ids, I've removed the id-attributes from the HTML.

I won't post my full HTML since it's quite long but the container with the button looks like the following:

<div >
       
        
        <h6 ><em >content</em>content</h6>
        
        <p ><span > Lorem ipsum </span></p>
            <button >read more</button>
    </div>  

And JS:

const readMoreBtn = document.getElementsByClassName('.readMore');
const text = document.querySelector('.main-p');

readMoreBtn.addEventListener('click', () => {
  text.classList.toggle('show-more');
  if(readMoreBtn.innerHTML === "read more") {
    readMoreBtn.innerHTML = "read less"
  } else {
    readMoreBtn.innerHTML = "read more"
  }
})


Thank you for your help and if you have any suggestions of how to improve my way of asking questions, shoot.

EDIT: I have multiple buttons, and all of them have a unique text. My goal is it to have the buttons display their unique text below them somehow. Here's another HTML to show what I mean:

<div >


        <h6 ><em >content</em>content</h6>

        <p ><span > Lorem ipsum </span></p>
            <button >read more</button>
    </div> 

<div >


        <h6 ><em >content</em>content</h6>

        <p ><span > Messi GOAT </span></p>
            <button >read more</button>
    </div> 

<div >


        <h6 ><em >content</em>content</h6>

        <p ><span > Ipsum Lorem </span></p>
            <button >read more</button>
    </div> 

With this JS Code I'm able to expand the first main-p only, but not the others.

const yourFunction = (e)=>{


  const text = document.querySelector('.main-p');
  text.classList.toggle('show-more');
  if(e.target.innerHTML === "read more") {
    e.target.innerHTML = "read less"
  } else {
    e.target.innerHTML = "read more"
  }


}

In my head there are two solutions that might work:

  1. I would have to connect each button to their main-p somehow in HTML? I've tried it but it didn't work.

  2. Working with loops in JS.

Is one of those two possible? TIA

CodePudding user response:

There are many ways to do this. Try this one.

const yourFunction = (e) => {

  const text = document.querySelector('.main-p');
  text.classList.toggle('show-more');
  if (e.target.innerHTML === "read more") {
    e.target.innerHTML = "read less"
  } else {
    e.target.innerHTML = "read more"
  }

}
<div >

  <h6 ><em >content</em>content</h6>

  <p ><span > Lorem ipsum </span></p>
  <button  onclick="yourFunction(event)">read more</button>
</div>

CodePudding user response:

getElementsByClassName() returns an HTMLCollection.

And querySelectorAll() returns a NodeList.

By contrast querySelector() and getElementById() return a DOM Element.

Each of these types are accessed and manipulated differently. You are treating the HTMLCollection and the NodeList as an Element.

One way to access every item in an HTMLCollection and NodeList is to first convert them to an array using Array.from(), then use forEach() on the resulting array to iterate each Element, in this case adding a click event handler to the element:

const readMoreBtn = document.getElementsByClassName('readMore');
const text = document.querySelectorAll('.main-p');
console.log(Array.from(text));

Array.from(readMoreBtn).forEach(function(elem) {
  elem.addEventListener('click', ({target}) => {
    target.textContent = ('read more' === target.textContent) ? 'read less' : 'read more';
  });
});
<p ><span > Lorem ipsum 1</span></p>
<button >read more</button>
<p ><span > Lorem ipsum 2</span></p>
<button >read more</button>
<p ><span > Lorem ipsum 3</span></p>
<button >read more</button>

CodePudding user response:

I prefer to use less JS, but more CSS. (more flexible)

Example, just add .is-acitve in container (parent) so just styling in CSS

const buttons = document.querySelectorAll('.main-container .readMore');

const setContainerActive = (el) => {
  el.target.closest('.main-container').classList.toggle('is-active')
}

Array.from(buttons).forEach((el) => {
  el.addEventListener('click', setContainerActive)
});
.main-container {
   /* Defaults */
}
.main-container.is-active {
  /* Active */
  background: red;
}
/* Example */
.main-container .show-is-active { display:none; }
.main-container.is-active .show-is-active { display: block; }
.main-container .hide-is-active { display:block; }
.main-container.is-active .show-hide-active { display: none; }
<div >
   <h6 ><em >content</em>content</h6>
   <p ><span > Lorem ipsum </span></p>
   <button ><span >read less</span><span >read more</span></button>
</div> 

  • Related