2

I want to close slide panel after click outside it or on X button inside, I have working code but there is one issue, if inside panel there is any element with class or ID after click on it panel also is closed. Here is my fiddle https://jsfiddle.net/ayfsgje1/1/

and my code:

const open = document.getElementById("open")
const slide = document.getElementById("slide")
const close = document.getElementById("close")

open.onclick = function() {
    slide.classList.toggle("active")
}

document.onclick = function(e) {
    if(e.target.id !== "slide" && e.target.id !== "open") {
        slide.classList.remove("active")
    }
}
.main {
  max-width:600px;
  overflow:hidden;
}
#slide {
  position:absolute;
  top:0;
  right:0;
  width:400px;
  height:100vh;
  transform: translateX(100%);
  border: 1px solid;
}
#slide.active {
  transform: translateX(0);
}
<div class="main">
  <span id="open">Click to open</span>
  <div id="slide">
    <div id="close">X</div>
    <span class="text">Lorem ipsum dolor sit amet consectetur adipisicing elit. Exercitationem incidunt asperiores quo architecto consectetur pariatur illum amet recusandae quod repudiandae eum neque, illo minus culpa aut voluptatibus dicta. Aspernatur, soluta?
   </span>
    Aliquid, sit alias. Veniam ullam deleniti aut ipsum ipsa repudiandae architecto tempora suscipit voluptates? Earum quaerat nam dignissimos cumque perspiciatis assumenda? Exercitationem aspernatur officiis vel earum consequatur illo ullam minima!
    
  </div>
</div>
s.kuznetsov
  • 14,870
  • 3
  • 10
  • 25
Agnes Tom
  • 356
  • 4
  • 23
  • Using `console.log(e.target.id)` inside the onclick function and clicking around, it seems like the target is the slide only if you click in the bottom of the slide. Probably a css issue? – fynsta May 05 '21 at 20:53

4 Answers4

2

Use a slightly modified structure. Also add targeting for the close by adding || close.contains(e.target) to the if () { ... } condition. Like this:

!slide.contains(e.target) && !open.contains(e.target)) || close.contains(e.target)

It works reliably.

const open = document.getElementById("open");
const slide = document.getElementById("slide");
const close = document.getElementById("close");

open.onclick = function () {
    slide.classList.toggle("active");
};

document.onclick = function (e) {
    if (!slide.contains(e.target) && !open.contains(e.target) || close.contains(e.target)) {
        slide.classList.remove("active");
    }
};
.main {
    max-width: 600px;
    overflow: hidden;
}

#slide {
    position: absolute;
    top: 0;
    right: 0;
    width: 400px;
    height: 100vh;
    transform: translateX(100%);
    border: 1px solid;
}

#slide.active {
    transform: translateX(0);
}
<div class="main">
    <span id="open">Click to open</span>
    <div id="slide">
        <div id="close">X</div>
        <span class="text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Exercitationem incidunt asperiores quo architecto consectetur pariatur illum amet recusandae quod repudiandae eum neque, illo minus culpa aut voluptatibus dicta.
            Aspernatur, soluta?
        </span>
        Aliquid, sit alias. Veniam ullam deleniti aut ipsum ipsa repudiandae architecto tempora suscipit voluptates? Earum quaerat nam dignissimos cumque perspiciatis assumenda? Exercitationem aspernatur officiis vel earum consequatur illo
        ullam minima!
    </div>
</div>
s.kuznetsov
  • 14,870
  • 3
  • 10
  • 25
1

You could, instead of matching the slide id, look up if an ancestor is the target "slide" or if the target has the "open" id. Shouldn't be too expensive.

const open = document.getElementById("open");
const slide = document.getElementById("slide");
const close = document.getElementById("close");

document.onclick = function(e) {
    if(e.target.id == "open") 
     slide.classList.toggle("active");
    else if (e.target.id == "close" || e.target.closest("#slide") == null)
     slide.classList.remove("active");
}
muzzletov
  • 640
  • 4
  • 13
1

Can you try this?

So, the idea is to add an overlay and listen on that element click instead of the document.

const open = document.getElementById("open")
const slide = document.getElementById("slide")
const close = document.getElementById("close")
const overlay = document.getElementById("overlay")

open.onclick = function() {
    slide.classList.toggle("active")
    overlay.classList.toggle("active")
}

document.onclick = function(e) {
    if(e.target.id === "overlay" || e.target.id === "close") {
        slide.classList.remove("active")
        overlay.classList.remove("active")
    }
}
.main {
  max-width:600px;
  overflow:hidden;
}
#slide {
  position:absolute;
  top:0;
  right:0;
  width:500px;
  height:100vh;
  transform: translateX(100%);
  border: 1px solid;
  background-color: #ffffff;
 
}
#slide.active {
  transform: translateX(0);
   z-index: 5 !important;
}

#overlay.active{
  position:absolute;
  top:0;
  right:0;
  width:100vw;
  height:100vh;
  background-color: transperent;
  z-index: 1;

}
<div class="main">
  <span id="open">Click to open</span>
  
  <div id="overlay">
    <div id="slide">
    <div id="close">X</div>
    <span class="text">Lorem ipsum dolor sit amet consectetur adipisicing elit. Exercitationem incidunt asperiores quo architecto consectetur pariatur illum amet recusandae quod repudiandae eum neque, illo minus culpa aut voluptatibus dicta. Aspernatur, soluta?
   </span>
    Aliquid, sit alias. Veniam ullam deleniti aut ipsum ipsa repudiandae architecto tempora suscipit voluptates? Earum quaerat nam dignissimos cumque perspiciatis assumenda? Exercitationem aspernatur officiis vel earum consequatur illo ullam minima!
    
  </div>
  </div>
</div>
Raj Thakar
  • 198
  • 9
1

I did some modification to your onclick event handler.
(Here's my attempt: https://jsfiddle.net/v7eL58bk/)

document.onclick = function(e) {
    const clickedOutsideElement = e.target.closest("#slide") === null;
    const closePressed = e.target.id === "close";
    const shouldClose = clickedOutsideElement || closePressed;
    if(e.target.id !== "open" && shouldClose) {
        slide.classList.remove("active")
    }
}

The clickedOutsideElement variable checks through the closest() method if the target clicked is outside the element with the id #slide.
The closePressed variable checks if the target clicked is the close button.
The shouldClose variable tells if the slide should close (it checks if the user has clicked outside the slide OR clicked the X button).
Finally in the if e.target.id !== "open" prevents the slide to close after clicking on "Click to open". In few words the if checks "if the user hasn't clicked on the button that opens the slide AND if the slide should close according to the rules"

Mark Karrica
  • 130
  • 8