Закрытие элемента при нажатии за пределами div или его кнопки открытия с помощью Vanilla JS

#javascript #html #css

Вопрос:

У меня есть две кнопки, одна из них открывает форму, а другая-раскрывающийся список.

Я хочу, чтобы оба элемента закрывались при нажатии снаружи, раскрывающийся список в порядке, потому что я хочу, чтобы он закрывался всякий раз, когда пользователь нажимает вне кнопки открытия.

Но второе не работает, я хочу, чтобы оно закрылось, когда эта логическая формула верна:

  • (кнопка открытия «fulfillSetButton» не нажата) ИЛИ (форма «Форма данных» не нажата)

Поэтому я сделал прослушиватель событий для всего HTML с формулой внутри оператора if:

 html.addEventListener("click", function(e){
  if(e.target !== (dateForm || fulfillSetButton)){
    dateForm.classList.remove("active");
  }
});
 

Но это не дает никаких идей о том, что не так?

 // All the elements
const dropdownButton = document.querySelector("#dropdownToggle"),
      fulfillSetButton = document.querySelector("#fulfillSetButton"),
      dropdownMenu = document.querySelector('.dropdown-menu'),
      html = document.querySelector("html"),
      dateForm = document.querySelector(".completion-date");
      

// Preventing default action of submiting the form
function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault) {
    e.preventDefault();
  } else {
    e.returnValue = false;
  }
}

// Closing the dropdown *working*
dropdownButton.addEventListener("click", function () {
    dropdownMenu.classList.toggle("show");
});

html.addEventListener("click", function(e){
  if(e.target !== dropdownButton){
    dropdownMenu.classList.remove("show");
  }
});

// Opening the date form
fulfillSetButton.addEventListener("click", function() {
    dateForm.classList.add("active");
});

// Closing the date form by submiting
dateForm.lastElementChild.addEventListener(
  "click", function () {
    preventDefault();
    dateForm.classList.remove("active");
  }
);

// Closing the date form by clicking outside *not working*
html.addEventListener("click", function(e){
  if(e.target !== (dateForm || fulfillSetButton)){
    dateForm.classList.remove("active");
  }
}); 
 :root {
  --trans-left:#84fab0;
  --trans-right:#8fd3f4;
  --background: #fff;
  --color: #222;
}

.button-list {

  align-self: center;
}

.completion-date {

  display: block;
    z-index: 10;
    position: fixed;
    top: -100%;
    left: calc(50% - 121px);
    min-width: max-content;
    padding: 1rem;
    border-radius: 0.5rem;
    background: var(--background);
    text-align: center;
    box-shadow: 0 0 18px -6px var(--color);
    transition: 0.3s;
}

.completion-date.active {

  top: calc(50% - 264px);
}

.completion-date div {

    margin: 0.5rem;
}

.completion-date input,
.completion-date button {

    margin: auto 0;
}

.completion-date input {

  width: 2rem;
  text-align: center;
}

.completion-date div > button {

  width: 1.5rem;
  margin: auto 3px;
  box-shadow: none;
  font-weight: bold;
}

section {

  width: 100vw;
}

input {

  box-shadow: inset 0 3px 7px -3px black;
  border: none;
  line-height: 2rem;
}

button {

  padding: 9px;
  border: none;
  box-shadow: 7px 7px 9px -10px var(--color), inset 0 0 15px -12px var(--color);
  border-radius: 6px;
}

.entry {

  display: flex;
  align-items: flex-end;
  height: 30vh;

  background-image: linear-gradient(120deg, var(--trans-left) 0%, var(--trans-right) 100%);
}

@media screen and (max-height: 660px) {

    .entry {

      height: 38vh;
    }
}

form {

  display: flex;
  flex-wrap: wrap;

  max-width: 36rem;

  justify-content: center;
  margin: 0 auto;
}

form > * {

  max-height: 100%;
}

form > div {

  display: flex;
}

form > input {

  margin: 0.5rem;
  box-shadow: inset 0px 3px 9px -4px #000000;
  border-radius: 6px;
}

.second-item {

  flex-wrap: wrap;
  justify-content: center;
}

.second-item > * {

  margin: 0.5rem;
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
  border-radius: 6px;
}

.dropdown-wrap > *:first-child {
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
border-radius: 6px;
}


.dropdown-wrap > *:first-child {
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
border-radius: 6px;
}

.btn.dropdown {

  transition: 0.3s ease;
}

.btn.dropdown.low {

  background-color: #84fab0;
}

.btn.dropdown.high {

  background-color: #ffa0a0;
}

.dropdown-menu {

  z-index: 2;
  position: absolute;

  margin-top: 9px;

  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;

  background: #fff;
  box-shadow: 1px 1px 20px -9px black;
  border-radius: 9px;

  pointer-events: none;
  opacity: 0;

  transition: 0.3s ease;
}

.dropdown-menu > li {

  padding: 0.4rem;
  cursor: pointer;
}

.dropdown-menu.show {

  pointer-events: all;
  opacity: 1;
} 
 <section class="entry">
        <form autocomplete="off">
          <input type="text" id="taskText">
          <div class="second-item">
            <button type="button" id="fulfillSetButton">Date form</button>
            <div class="dropdown-wrap">
              <button class="btn dropdown" type="button" id="dropdownToggle">Dropdown button</button>
              <ul class="dropdown-menu" id="taskPriority" style="background: linear-gradient(0deg, rgba(132,250,176,1) 0%, rgba(132,250,176,1) 33%, rgba(255,255,255,1) 33%, rgba(255,255,255,1) 50%, rgba(255,255,255,1) 66%, rgba(255,160,160,1) 66%, rgba(255,160,160,1) 100%); color: #000">
                <li>Vysoká</li>
                <li>Střední</li>
                <li>Nízká</li>
              </ul>
            </div>
            <button type="submit">submit</button>
          </div>
        </form>
        <form class="completion-date">
        <h2>Dokončit za:</h2>
        <div>
          <button onclick="increaseValue(months)"> </button>
          <input id="months" type="number" value="0" min="0" max="12">
          <button onclick="decreaseValue(months)">-</button>
          <p>měsíců</p>
        </div>
        <div>
          <button onclick="increaseValue(weeks)"> </button>
          <input id="weeks" type="number" value="0" min="0" max="5">
          <button onclick="decreaseValue(weeks)">-</button>
          <p>týdnů</p>
        </div>
        <div>
          <button onclick="increaseValue(days)"> </button>
          <input id="days" type="number" value="0" min="0" max="31">
          <button onclick="decreaseValue(days)">-</button>
          <p>dní</p>
        </div>
        <div>
          <button onclick="increaseValue(hours)"> </button>
          <input id="hours" type="number" value="0" min="0" max="23">
          <button onclick="decreaseValue(hours)">-</button>
          <p>hodin</p>
        </div>
        <div>
          <button onclick="increaseValue(minutes)"> </button>
          <input id="minutes" type="number" value="0" min="0" max="59">
          <button onclick="decreaseValue(minutes)">-</button>
          <p>minut</p>
        </div>
        <button>Nastavit</button>
      </form>
      </section> 

Ответ №1:

Попробуй это:

 window.addEventListener('click', function(e){
if( div.contains(e.target)){
    //click inside of element  
} else{
    //click outside of element
  }
});
 

Я исправил некоторые CSS и HTML.

 // All the elements
const dropdownButton = document.querySelector("#dropdownToggle"),
      fulfillSetButton = document.querySelector("#fulfillSetButton"),
      dropdownMenu = document.querySelector('.dropdown-menu'),
      html = document.querySelector("body"),
      dateForm = document.querySelector("#completion-date-id");
      

// Preventing default action of submiting the form
function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault) {
    e.preventDefault();
  } else {
    e.returnValue = false;
  }
}

// Closing the dropdown *working*
dropdownButton.addEventListener("click", function () {
    dropdownMenu.classList.toggle("show");
});
//new way works every spot on page
window.addEventListener('click', function(e){
if( dropdownButton.contains(e.target)){
  } else{
    dropdownMenu.classList.remove("show");
  }
});
//Old way
//html.addEventListener("click", function(e){
//  if(e.target !== dropdownButton){
//    dropdownMenu.classList.remove("show");
//  }
//});

// Opening the date form
fulfillSetButton.addEventListener("click", function() {
    dateForm.classList.add("active");
});

// Closing the date form by submiting
dateForm.lastElementChild.addEventListener(
  "click", function () {
    preventDefault();
    dateForm.classList.remove("active");
  }
);

// Closing the date form by clicking outside *working now*
window.addEventListener('click', function(e){
    
if( dateForm.contains(e.target) || fulfillSetButton.contains(e.target)){
  } else{
    dateForm.classList.remove("active");
  }
});
// Closing the date form by clicking outside *not working*
//html.addEventListener("click", function(e){
//  if(e.target !== (dateForm || fulfillSetButton)){
//    dateForm.classList.remove("active");
//  }
//}); 
 :root {
  --trans-left:#84fab0;
  --trans-right:#8fd3f4;
  --background: #fff;
  --color: #222;
}

.button-list {

  align-self: center;
}

.completion-date {
    z-index: 10;
    position: fixed;
    top: -100%;
    left: calc(50% - 121px);
    min-width: max-content;
    padding: 1rem;
    border-radius: 0.5rem;
    background: var(--background);
    text-align: center;
    box-shadow: 0 0 18px -6px var(--color);
    transition: 0.3s;
}

.completion-date.active {

  top: calc(50% - 264px);
}

.completion-date div {

    margin: 0.5rem;
}

.completion-date input,
.completion-date button {

    margin: auto 0;
}

.completion-date input {

  width: 2rem;
  text-align: center;
}

.completion-date div > button {

  width: 1.5rem;
  margin: auto 3px;
  box-shadow: none;
  font-weight: bold;
}

section {

  width: 100vw;
}

input {

  box-shadow: inset 0 3px 7px -3px black;
  border: none;
  line-height: 2rem;
}

button {

  padding: 9px;
  border: none;
  box-shadow: 7px 7px 9px -10px var(--color), inset 0 0 15px -12px var(--color);
  border-radius: 6px;
}

.entry {

  display: flex;
  align-items: flex-end;
  height: 30vh;

  background-image: linear-gradient(120deg, var(--trans-left) 0%, var(--trans-right) 100%);
}

@media screen and (max-height: 660px) {

    .entry {

      height: 38vh;
    }
}

form {

  display: flex;
  flex-wrap: wrap;

  max-width: 36rem;

  justify-content: center;
  margin: 0 auto;
}

form > * {

  max-height: 100%;
}

form > div {

  display: flex;
}

form > input {

  margin: 0.5rem;
  box-shadow: inset 0px 3px 9px -4px #000000;
  border-radius: 6px;
}

.second-item {

  flex-wrap: wrap;
  justify-content: center;
}

.second-item > * {

  margin: 0.5rem;
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
  border-radius: 6px;
}

.dropdown-wrap > *:first-child {
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
border-radius: 6px;
}


.dropdown-wrap > *:first-child {
  box-shadow: 4px 4px 8px -6px #000000, -4px -4px 10px -3px #FFFFFF;
border-radius: 6px;
}

.btn.dropdown {

  transition: 0.3s ease;
}

.btn.dropdown.low {

  background-color: #84fab0;
}

.btn.dropdown.high {

  background-color: #ffa0a0;
}

.dropdown-menu {

  z-index: 2;
  position: absolute;

  margin-top: 9px;

  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;

  background: #fff;
  box-shadow: 1px 1px 20px -9px black;
  border-radius: 9px;

  pointer-events: none;
  opacity: 0;

  transition: 0.3s ease;
}

.dropdown-menu > li {

  padding: 0.4rem;
  cursor: pointer;
}

.dropdown-menu.show {

  pointer-events: all;
  opacity: 1;
}
.active{
display: block;
} 
 <body>
<section class="entry">
        <form autocomplete="off">
          <input type="text" id="taskText">
          <div class="second-item">
            <button type="button" id="fulfillSetButton">Date form</button>
            <div class="dropdown-wrap">
              <button class="btn dropdown" type="button" id="dropdownToggle">Dropdown button</button>
              <ul class="dropdown-menu" id="taskPriority" style="background: linear-gradient(0deg, rgba(132,250,176,1) 0%, rgba(132,250,176,1) 33%, rgba(255,255,255,1) 33%, rgba(255,255,255,1) 50%, rgba(255,255,255,1) 66%, rgba(255,160,160,1) 66%, rgba(255,160,160,1) 100%); color: #000">
                <li>Vysoká</li>
                <li>Střední</li>
                <li>Nízká</li>
              </ul>
            </div>
            <button type="submit">submit</button>
          </div>
        </form>
        <form id="completion-date-id" class="completion-date active">
        <h2>Dokončit za:</h2>
        <div>
          <button onclick="increaseValue(months)"> </button>
          <input id="months" type="number" value="0" min="0" max="12">
          <button onclick="decreaseValue(months)">-</button>
          <p>měsíců</p>
        </div>
        <div>
          <button onclick="increaseValue(weeks)"> </button>
          <input id="weeks" type="number" value="0" min="0" max="5">
          <button onclick="decreaseValue(weeks)">-</button>
          <p>týdnů</p>
        </div>
        <div>
          <button onclick="increaseValue(days)"> </button>
          <input id="days" type="number" value="0" min="0" max="31">
          <button onclick="decreaseValue(days)">-</button>
          <p>dní</p>
        </div>
        <div>
          <button onclick="increaseValue(hours)"> </button>
          <input id="hours" type="number" value="0" min="0" max="23">
          <button onclick="decreaseValue(hours)">-</button>
          <p>hodin</p>
        </div>
        <div>
          <button onclick="increaseValue(minutes)"> </button>
          <input id="minutes" type="number" value="0" min="0" max="59">
          <button onclick="decreaseValue(minutes)">-</button>
          <p>minut</p>
        </div>
        <button>Nastavit</button>
      </form>
      </section>
</body> 

Комментарии:

1. Я также исправил некоторые ошибки JavaScript в некоторых частях.

2. Это сработало для вас?

3. Это так, спасибо, но не могли бы вы объяснить в ответе, почему эта функция работала, а моя-нет? Это действительно помогло бы мне понять это лучше, я не могу найти это в другом месте.

4. Это было связано с тем, что выбранный вами html-тег находился не на том же уровне, что и всплывающий элемент, и, насколько я знаю, использование опции окна-лучший способ сделать это. Пожалуйста, отметьте это как правильный ответ, если он решил вашу проблему. 🙂