0

I have constructed an accordion which just works fine but I want to close this if someone clicks outside the element in JavaScript

I tried to add a click listener that captures click outside the div and turn its display to off but it doesn't work as it considers the link itself an outside of the div link and thus stops opening the div

function toggleaccordition() {
  var link = document.getElementById('acrd_link');
  var accrdion = document.getElementById("acc");
  link.classList.toggle('active');
  accrdion.classList.toggle('active');
}
nav ul {
  display: flex;
}

nav ul li {
  display: block;
  margin-right: 20px;
  margin-left: 2px;
  list-style: none;
  text-decoration: none;
}

nav ul li a {
  text-decoration: none;
}

#acrd_link.active {
  text-decoration: underline;
  color: deepskyblue;
}

.accrd {
  display: block;
  height: 0px;
  width: 450px;
  color: blue;
  opacity: 0.0;
  transition: opacity 0.5s;
  transition: height 0.5s;
  padding-top: 30px;
  position: relative;
  z-index: 4;
  background: #ff0036;
}

.internal {
  background: gray;
}

.internal span {
  color: yellow;
}

.accrd>* {
  display: none;
}

.accrd.active {
  display: block height: 500px;
  opacity: 1.0;
  transition: height 1s;
  transition: opacity 1s;
}

.accrd.active>* {
  display: block;
}
<nav>
  <ul>
    <li><a href="#">xxxxxxx</a></li>
    <li><a href="#" id="acrd_link" onclick="toggleaccordition()">Click two open/close accordion</a></li>
    <li><a href="#">yyyyyyyy</a></li>
  </ul>
</nav>
<div class="accrd" id="acc">
  <div class="internal">
    This is my accordion that works fine.<span>I just want this to be closed when i click outside this accrdion<span></div>
</div>
Tyler Roper
  • 21,445
  • 6
  • 33
  • 56
gyanendra
  • 83
  • 7
  • [focusout event](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) might be what you are looking for – Wendelin Oct 02 '19 at 17:52
  • Possible duplicate of [How to make a closing accordion when you click outside of it?](https://stackoverflow.com/questions/18421214/how-to-make-a-closing-accordion-when-you-click-outside-of-it) – Calvin Nunes Oct 02 '19 at 17:57
  • focusout i doubt will not work as the accordion is never in focus. it just translate height and opacity and then teh display becomes invisible@wendelin – gyanendra Oct 02 '19 at 17:59
  • @CalvinNunesI tried it, it is not working – gyanendra Oct 02 '19 at 18:01

1 Answers1

3

Simply add an event listener on the document that closes the accordion after verifying that it wasn't the accordion you clicked on.

Also, don't use hyperlinks when clicking them won't result in navigation - - that is semantically incorrect and will mess with screen readers. Just apply the click event to any other element that you like.

Lastly, don't do your event handling inline with HTML (onclick). Here's why. Do all the event binding in JavaScript.

// Set up DOM element references outside of callbacks so you only
// go get the references one time, not every time the callback runs.
var link = document.getElementById('acrd_link');
var accordion = document.getElementById("acc");
var internal = document.querySelector(".internal");

link.addEventListener("click", toggleaccordition);

function toggleaccordition() {
  link.classList.toggle('active');
  accordion.classList.toggle('active');
}


// Set up handler for clicks that occur outside of the accordion
document.addEventListener("click", closeaccordition);

function closeaccordition(event) {
  // Since accordion is part of the document, we need to make
  // sure this click didn't originate with the accordion.
  if(event.target !== link && event.target !== accordion && event.target !== internal){
    link.classList.remove('active');
    accordion.classList.remove('active');
  }
}
nav ul {
  display: flex;
}

nav ul li {
  display: block;
  margin-right: 20px;
  margin-left: 2px;
  list-style: none;
  text-decoration: none;
  cursor:pointer;
}

#acrd_link.active {
  text-decoration: underline;
  color: deepskyblue;
}

.accrd {
  display: block;
  height: 0px;
  width: 450px;
  color: blue;
  opacity: 0.0;
  transition: opacity 0.5s;
  transition: height 0.5s;
  padding-top: 30px;
  position: relative;
  z-index: 4;
  background: #ff0036;
}

.internal {
  background: gray;
}

.internal span {
  color: yellow;
}

.accrd>* {
  display: none;
}

.accrd.active {
  display: block height: 500px;
  opacity: 1.0;
  transition: height 1s;
  transition: opacity 1s;
}

.accrd.active>* {
  display: block;
}
<nav>
  <ul>
    <li>xxxxxxx</li>
    <li id="acrd_link">Click two open/close accordion</li>
    <li>yyyyyyyy</li>
  </ul>
</nav>
<div class="accrd" id="acc">
  <div class="internal">
    This is my accordion that works fine.<span>I just want this to be closed when i click outside this accrdion</span>
   </div>
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • It closes but it closes too even someone clicks on the accordion – gyanendra Oct 02 '19 at 19:39
  • 1
    @gyanendra Updated so that won't happen. – Scott Marcus Oct 02 '19 at 19:45
  • What if i have a bunch of things in my accordion, like 3 rows, 6 columns and images inside them, description below images and one foot row for accordion, links inside the row? – gyanendra Oct 02 '19 at 20:08
  • 1
    You can determine if any of those items were the source by giving them all the same class and then your test becomes simple `if(!event.target.classList.contains("theClassName"))`. – Scott Marcus Oct 02 '19 at 20:43
  • Yes, the classname works but in this case its good. But there is still some problems like if at the place of accordion, i want the self closing feature to be added in a div that is a modal. it contain many elements as classes and are styled targeting the class. They have a bunch of child elements. In this case I have to target each element seperately. Cant I target them all?@Scott Marcus – gyanendra Oct 03 '19 at 05:46
  • 1
    @gyanendra We're getting further away from the scenario you posted, so you may want to post a new question or edit this one with the actual criteria you will have. But, the short answer is that there are numerous ways to handle this issue. It really depends on your specific use case, so I would need to see your HTML structure to know which technique would be best. – Scott Marcus Oct 03 '19 at 13:32