Home > Mobile >  Toggle Rotate Arrows on Click
Toggle Rotate Arrows on Click

Time:08-06

I'm creating multiple arrows that rotate when clicked. However, I'm getting an error "div.addEventListener is not a function" when using an addEventListener. Is there a way to make this work? Thanks in advance for any help.

(function (document) {
  var div = document.getElementsByClassName("container");
  var icon = document.getElementsByClassName("arrow");
  var open = false;

  div.addEventListener("click", function () {
    if (open) {
      icon.className = "fa fa-arrow-down";
    } else {
      icon.className = "fa fa-arrow-down rotate";
    }

    open = !open;
  });
})(document);
.fa-arrow-down {
  transform: rotate(0deg);
  transition: transform 1s linear;
}

.fa-arrow-down.open {
  transform: rotate(180deg);
  transition: transform 1s linear;
}
<div >
  <i ></i>
</div>

<div >
  <i ></i>
</div>

<div >
  <i ></i>
</div>

CodePudding user response:

Your issue was caused by you trying to bind event to an array instead of the element due to getElementsByClassName will return HTMLCollection which is type of array.

You can refer this answer which explained why bind event to array was not working.

I assuming you are using Vanilla JS only, first get all the elements which you want trigger the click event by class name, which is container class:

/* getElementsByClassName return array */
var divs = document.getElementsByClassName("container");

Then you have to loop though these div:

/* loop through divs array */
Array.from(divs).forEach(function (div) {
    /* only you bind "click" event to "div" at here */
});

And here only you bind "click" event to your div, following code will find the arrow class elements in the container class and toggle the open class.

div.addEventListener("click", function (event) {
    /* get the clicked element, which is the "container" */
    const clickedElement = event.currentTarget;

    /* find "arrow" class elements in "container" */
    const arrows = clickedElement.getElementsByClassName("arrow");

    /* loop through each "arrow" class element and toggle the "open" class name */
    Array.from(arrows).forEach(function (arrow) {
        arrow.classList.toggle("open");
    });
});

Demo:

(function(document) {
  /* getElementsByClassName return array */
  var divs = document.getElementsByClassName("container");

  /* loop through divs array, bind the click event to each div */
  Array.from(divs).forEach(function(div) {
    div.addEventListener("click", function(event) {
      /* get the clicked element, which is the "container" */
      const clickedElement = event.currentTarget;

      /* find "arrow" class elements in "container" */
      const arrows = clickedElement.getElementsByClassName("arrow");

      /* loop through each "arrow" class element and toggle the "open" class name */
      Array.from(arrows).forEach(function(arrow) {
        arrow.classList.toggle('open');
      });
    });
  });
})(document);
.fa-arrow-down {
  transform: rotate(0deg);
  transition: transform 1s linear;
}

.fa-arrow-down.open {
  transform: rotate(180deg);
  transition: transform 1s linear;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" integrity="sha512-1sCRPdkRXhBV2PBLUdRb4tMg1w2YPf37qatUFeS7zlBy7jJI8Lf4VHwWfZZfpXtYSLy85pkm9GaYVYMfw5BC1A==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div >
  <i ></i>
</div>

<div >
  <i ></i>
</div>

<div >
  <i ></i>
</div>

CodePudding user response:

The getElementsByClassName() method of Document interface returns an array-like object of all child elements which have all of the given class name. It returns array like object, so way of accessing that div should be like accessing certain element of array.

You can use for loop

(function (document) {
  const div = document.getElementsByClassName("container");
  const divLen = div.length;
  let open = false;

  for(let i = 0; i < divLen;   i) {
    div[i].addEventListener("click", function () {
      const icon = this.getElementsByClassName("arrow");
      if (open) {
        icon.className = "fa fa-arrow-down";
      } else {
        icon.className = "fa fa-arrow-down rotate";
      }

      open = !open;
    }); 
  }
})(document);

Here's ES6 example of forEach() loop

(function(document) {
  const div = document.getElementsByClassName("container");
  let open = false;

  div.forEach(function(elem) { 
    elem.addEventListener("click", function() {
      const icon = elem.getElementsByClassName("arrow")[0];
      if (open) {
        icon.className = "fa fa-arrow-down";
      } else {
        icon.className = "fa fa-arrow-down rotate";
      }
     open = !open;
    });
  });
})(document);

  • Related