假设我有一系列详细信息HTML元素,当切换一个元素时,所有这些元素我都想切换为打开状态。每个元素看起来都像这样:
<details class="basic" data-target="basic">
<summary>Click me 1</summary>
Some descriptive text 1
<details>
我在每个元素的切换事件上添加一个事件侦听器:
addEventListeners();
function addEventListeners(){
var deets = document.getElementsByTagName("details");
for(var i = 0; i < deets.length; i++){
deets[i].addEventListener("toggle", togGroups, false);
}
}
function togGroups(event){
var targetClass = this.getAttribute("data-target"),
elements = document.getElementsByClassName(targetClass),
open = this.open; //currently open or not?
for(var i = 0; i < elements.length; i++){
let el = elements[i];
//Remove the event listener to stop triggering
//the toggle event when setting every element's "open"
//status
el.removeEventListener("toggle", togGroups, false);
el.open = open;
}
//Scroll the originally-toggled element into view
this.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
//Re-add event listeners for the next toggle
addEventListeners();
}
此代码可以打开所有元素,但是我无法弄清楚如何滚动到最初切换的元素。而是滚动到最后一个切换元素-基本上是页面底部。我认为,如果在for ..循环中未删除任何事件侦听器,则该行为将是有意义的-每当“ open”属性设置为true时,都会触发每个元素的toggle事件,因此该函数将触发多次并最终“这”将是最后一个元素。但是,我试图将其删除,然后仅在打开每个元素之后才将其重新设置。
顺便说一句,如果我删除了最后的addEventListeners()行,它可以正确滚动,但随后我将失去再次单击的能力。
我将不胜感激。
非常感谢。
答案 0 :(得分:2)
我们不需要删除任何事件侦听器,也不需要绑定到每个<details>
。我们需要的是一个注册到祖先标签*的事件监听器,用于无限数量的后代标签。
详细信息在演示中进行了评论。 <details>
中也有更多详细信息。
* 它甚至可以是窗口或文档,但除非是用于关键事件,否则不建议使用
// Reference <main>
const main = document.querySelector('main');
// Register <main> to click event -- fire callback
main.addEventListener('click', callback);
// Callback pass the Event Object
function callback(event) {
// Prevent <details> from toggling it's own [open]
event.preventDefault();
// Reference the clicked tag
const tgt = event.target;
// Collect all <details> into a NodeList
const dtl = document.querySelectorAll('details');
// Iterate through NodeList -- on each <details> toggle [open]
for (let detail of dtl) {
detail.toggleAttribute('open');
}
// Scroll clicked tag into center view
tgt.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// Terminate function
return false;
}
:root {
font: 400 16px/1.2 Arial
}
body {
overflow-x: hidden;
overflow-y: scroll;
}
main {
padding: 40vh 0;
}
details {
cursor: pointer;
margin: 15px auto;
border-bottom: 4px ridge #444;
border-top: 4px ridge #444;
padding: 5px 5px 10px
}
details:focus-within {
outline: 3px solid cyan;
}
summary {
font-family: Verdana;
padding: 5px;
border-right: 5px ridge #444;
border-left: 5px ridge #444;
}
<main>
<details>
<summary>Read More...</summary>
Register an event to an ancestor tag that all targeted tags share in common. In this demo that ancestor tag is <code><main></code>.
</details>
<details>
<summary>Further Details</summary>
Write the callback function so that it only reacts to events triggered on specific tags (ie <code><details></code>) and ignore the rest. This is possible because the click event propigates (or bubbles).
</details>
<details>
<summary>Code Description</summary>
This programming pattern is called Event Delegation.
</details>
</main>
答案 1 :(得分:0)
您只需要添加一次发泄侦听器。.
addEventListeners();
function addEventListeners() {
var deets = document.getElementsByTagName("details");
for(var i = 0; i < deets.length; i++){
deets[i].addEventListener("toggle", togGroups, false);
}
}
function togGroups(event) {
var targetClass = this.getAttribute("data-target");
var elements = document.getElementsByClassName(targetClass);
for(var i = 0; i < elements.length; i++){
let el = elements[i];
el.open = this.open;
}
// Scroll the originally-toggled element into view
this.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}