详细信息标签可以通过单击外部来关闭,但不能通过单击摘要标签本身来关闭?

问题描述 投票:0回答:2

对于以下代码:

const allDropdowns = document.querySelectorAll(".main-nav__dropdown");

window.addEventListener("click", (e) => {
  [...allDropdowns].forEach((dropdown) => (dropdown.open = false));
});
<details class="main-nav__dropdown">
  <summary>First</summary>
  <ul>
    <li><a href="#">First</a></li>
    <li><a href="#">Second</a></li>
    <li><a href="#">Third</a></li>
    <li><a href="#">Fourth</a></li>
  </ul>
</details>

<details class="main-nav__dropdown">
  <summary>Second</summary>
  <ul>
    <li><a href="#">First</a></li>
    <li><a href="#">Second</a></li>
  </ul>
</details>

为什么每当我单击

summary
本身时下拉菜单都不会关闭?我怎样才能让它工作?

javascript html dropdown html-tag-details
2个回答
2
投票

更新

click
活动更改为
mousedown
到目前为止一切顺利 🤞

图A

document.addEventListener('mousedown', closeDD);

我认为这是因为事件处理程序将侦听窗口上的所有单击事件,其中包括所有

<summary>
,因此存在冲突。每次点击
<summary>
都会触发两个事件处理程序:

  1. 窗口关闭所有事件处理程序
    <details>
  2. 默认情况下内置的切换开关可以打开和关闭其父级
    <details>

手风琴的制作方式通常是使用内容框的outside按钮来折叠和展开。对于像

<details>
<summary>
这样的嵌套按钮,当您想要添加额外的事件处理程序来执行不同的操作时,单击事件会出现问题。我认为这就是为什么将 "toggle" 事件添加到 HTMLDetailsElement 界面的原因。它在
<details>
打开或关闭后立即触发。

在下面的示例中,有两个事件处理程序:

事件处理程序 活动 听众 目的
closeDD(e)
mousedown
document
当用户单击任意位置时,所有
<details>
将关闭
checkDD(e)
toggle
全部
<details>
如果用户单击
<summary>
,则会记录其索引,并且一旦所有
<details>
都已被
closeDD()
关闭。录制的
<details>
如果最初关闭,将会重新打开。

除了前面描述的行为之外,作为副作用,还实现了互斥性 - 基本上一次只能打开一个

<details>

我忘记添加最重要的修复之一,即

pointer-events
在活动的
<summary>
上被禁用,并在
<details>
关闭时重新启用。对于我的一生,我无法完全解决点击冲突,因此当
<details>
打开时,看起来像
<summary>
上的点击有效,但实际上是
<details>
对点击做出了反应。

详情已在示例中注释

// Collect all <details> in an array
const allDD = Array.from(document.querySelectorAll("details"));
// Define a flag outside of function
let status = -1;
// Bind document to the mousedown event
document.addEventListener('mousedown', closeDD);

function closeDD(e) {
  // Enable pointer-events on each <summary>
  allDD.forEach(dd => dd.firstElementChild.style.pointerEvents = 'auto');
  // Reference the tag that the user clicked
  const active = e.target;
  /*
  If the tag the user clicked is a <summary> AND it's parent <details>
  is [open]...
  ...Find the index of that <details> and assign it to the status flag
  */
  if (active.matches('summary') && !active.parentElement.hasAttribute('open')) {
    status = allDD.indexOf(active.parentElement);
  }
  // Next, close all <details>
  allDD.forEach(dd => dd.open = false);
}
// Bind all <details> to the toggle event
allDD.forEach(dd => dd.ontoggle = checkDD);

function checkDD(e) {
  // The current <details> tag
  const active = e.target;
  // Find it's index
  let position = allDD.indexOf(active);
  /*
  If the index matches the flag...
  ...The <details> opens...
  ...Disable pointer-events on <summary>
  */
  if (status === position) {
    active.open = true;
    active.firstElementChild.style.pointerEvents = 'none';
  }
  // Rest flag
  status = -1;
}
details {
  outline: 5px solid blue;
}

summary {
  outline: 3px dashed red;
  cursor: pointer;
  user-select: none;
}
<details>
  <summary>First</summary>
  <ul>
    <li><a href="https://example.com">First</a></li>
    <li><a href="https://example.com">Second</a></li>
  </ul>
</details>

<details>
  <summary>Second</summary>
  <ul>
    <li><a href="https://example.com">First</a></li>
    <li><a href="https://example.com">Second</a></li>
    <li><a href="https://example.com">Third</a></li>
  </ul>
</details>

<details>
  <summary>Third</summary>
  <ul>
    <li><a href="https://example.com">First</a></li>
    <li><a href="https://example.com">Second</a></li>       
    <li><a href="https://example.com">Third</a></li>
    <li><a href="https://example.com">Fourth</a></li>
  </ul>
</details>


1
投票

请检查下面的代码。

// create array with all elements to switch
const summarys = Array.from(document.querySelectorAll('details'));
// make var with information what element was clicked
let detailsIndex;

summarys.forEach((summary, index) => {
  summary.addEventListener('mousedown', ()=>{
      
      if(detailsIndex === index) {
        //console.log('clicked in same element');
        detailsIndex = index;
      }if(detailsIndex != index) {
        //console.log("clicked in other element")
        detailsIndex = index;
        summarys.forEach((summ) => summ.open = false);
      }if(detailsIndex == null) {
       // console.log('first time clicked')
        detailsIndex = index;
      }
  
  })
})
h2 {
  font-size: 15px;
}
p {
  font-size: 12px
}
.container {
  display: flex;
  width: 100%;
}
details {
  display: flex;
}
summary {
  display: flex;
  align-items: center;
  margin-left: 10px;
}
summary::after {
  display: flex;
  content: '+';
  margin-left: 20px;
  height: 20px;
  width: 20px;
}
<!DOCtype html>
<html>
<head>
</head>
<body>
  <div id='container'>
    <details>
      <summary>
        <h2>This is a first</h2>
      </summary>
         <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolore tempore quisquam alias             veritatis veniam debitis ad asperiores, nobis aliquam minima accusantium corrupti                 ipsam quasi quo,quam quidem.</p>
    </details>
    
    <details>
      <summary>
        <h2>This is a second</h2>
      </summary>
         <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolore tempore quisquam alias             veritatis veniam debitis ad asperiores, nobis aliquam minima accusantium corrupti                 ipsam quasi quo,quam quidem.</p>
    </details>
    
    <details>
      <summary>
        <h2>This is a third</h2>
      </summary>
         <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolore tempore quisquam alias             veritatis veniam debitis ad asperiores, nobis aliquam minima accusantium corrupti                 ipsam quasi quo,quam quidem.</p>
    </details>
  
  </div>
</body>
</html>

© www.soinside.com 2019 - 2024. All rights reserved.