如何通过 div 数组迭代点击事件
How to iterate with click event through Array of divs
我希望文本部分在单击时出现和消失。
第一次点击前只能看到banner,还没有经文;第一次单击时出现第一节,第二次单击时第二节出现在第一节的位置,依此类推。
我试图通过隐藏元素来实现这一点,将它们放在一个数组中,并让它们在调用函数的次数符合诗句的索引时显示。
我是 JavaScript 的新手,不了解抛出的异常。如果我尝试在线检查我的代码,当 O 尝试通过单击网站调用该函数时,我会收到此异常:
Uncaught TypeError: Cannot read properties of null (reading 'style')
到目前为止,这是我的代码:
const text = document.querySelector(".banner")
document.addEventListener('click', myFunction);
const verse1 = document.querySelector(".verse1")
const verse2 = document.querySelector(".verse2")
const verse3 = document.querySelector(".verse3")
const verse4 = document.querySelector(".verse4")
const verse5 = document.querySelector(".verse5")
const verses = [verse1, verse2, verse3, verse4, verse5]
let versesLength = verses.length;
function myFunction() {
for (let i = 0; i < versesLength; i++) {
text.innerHTML = verses[i].style.display = 'block';
}
}
<div class="banner">
<script src="main.js"></script>
<img src="files/SomeLogo.jpg" alt="We are still building on our Website:-)">
</div>
<div id="verses">
<div class="verse1" style="display: none">Lorem Ipsum</div>
<div class="verse2" style="display: none">Lorem Ipsum2</div>
<div class="verse3" style="display: none">Lorem Ipsum3</div>
<div class="verse4" style="display: none">Lorem Ipsum4</div>
<div class="verse5" style="display: none">Lorem Ipsum5</div>
</div>
我被卡住了,在过去的几个小时里点击了类似的问题。在此先感谢您的帮助
这应该可以做到:
const verses = document.querySelectorAll('.verse');
const banner = document.querySelector('.banner');
const length = verses.length;
let counter = 0;
document.onclick = () => {
if (counter === 0) banner.classList.add('hide');
if (counter >= length) return;
verses[counter].classList.add('show');
if (verses[counter - 1]) verses[counter - 1].classList.remove('show');
counter++;
};
body {
background: orange;
}
.hide {
display: none !important;
}
.show {
display: block !important;
}
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<div class="banner">
<img
src="files/SomeLogo.jpg"
alt="We are still building on our Website:-)"
/>
</div>
<div id="verses">
<div class="verse1 verse hide">Lorem Ipsum</div>
<div class="verse2 verse hide">Lorem Ipsum2</div>
<div class="verse3 verse hide">Lorem Ipsum3</div>
<div class="verse4 verse hide">Lorem Ipsum4</div>
<div class="verse5 verse hide">Lorem Ipsum5</div>
</div>
<script src="main.js"></script>
</body>
</html>
无需更改 HTML 中的任何内容,您可以在 javascript
中执行类似的操作
const text = document.querySelector(".banner")
document.addEventListener('click', myFunction);
let verses = document.querySelector("#verses").children
let count = 0
function myFunction() {
Array.from(verses).forEach(el=> el.style.display="none")
if(count < verses.length){
verses[count].style.display = 'block'
count ++
if(count===verses.length) count =0
}
}
您可以通过为所有经文元素提供相同的 class: verse
来消除对数组的需要。我们可以用 querySelectorAll
.
来抓取它们
在每节经文中添加 a data attribute 以识别它们。
为了限制全局变量的数量,我们可以使用闭包 - 在 addEventListener
中,我们调用初始化计数的 handleClick
函数,然后 returns 将分配给侦听器的功能。这是a closure。它维护着它返回时可以使用的外部词法环境(即变量)的副本。
// Cache the elements with the verse class
const banner = document.querySelector('.banner');
const verses = document.querySelectorAll('.verse');
// Call `handleClick` and assign the function it
// returns to the listener
document.addEventListener('click', handleClick());
function handleClick() {
// Initialise `count`
let count = 1;
// Return a function that maintains a
// copy of `count`
return function () {
// If the count is 5 or less
if (count < verses.length + 1) {
// Remove the banner
if (count === 1) banner.remove();
// Remove the previous verse
if (count > 1) {
const selector = `[data-id="${count - 1}"]`;
const verse = document.querySelector(selector);
verse.classList.remove('show');
}
// Get the new verse
const selector = `[data-id="${count}"]`;
const verse = document.querySelector(selector);
// And show it
verse.classList.add('show');
// Increase the count
++count;
}
}
}
.verse { display: none; }
.show { display: block; margin-top: 1em; padding: 0.5em; border: 1px solid #787878; }
[data-id="1"] { background-color: #efefef; }
[data-id="2"] { background-color: #dfdfdf; }
[data-id="3"] { background-color: #cfcfcf; }
[data-id="4"] { background-color: #bfbfbf; }
[data-id="5"] { background-color: #afafaf; }
<div class="banner">
<img src="https://dummyimage.com/400x75/404082/ffffff&text=We+are+still+building+our+website" alt="We are still building on our Website:-)">
</div>
<div>
<div data-id="1" class="verse">Lorem Ipsum 1</div>
<div data-id="2" class="verse">Lorem Ipsum 2</div>
<div data-id="3" class="verse">Lorem Ipsum 3</div>
<div data-id="4" class="verse">Lorem Ipsum 4</div>
<div data-id="5" class="verse">Lorem Ipsum 5</div>
</div>
其他文档
修复错误
出现“无法读取 null 的属性”错误是因为您尝试访问 null 的属性。您的数组包含空值,因为您试图在浏览器将元素插入其 DOM.
之前获取这些元素
浏览器解析 HTML 的方式与您阅读它的方式相同:从左到右,从上到下。
如果浏览器遇到常规 <script>
元素,它会停止解析并首先执行 JavaScript。当然,某些元素可能在 DOM.
中尚不可用
有多种延迟脚本执行的方法:
- 将attribute
defer
添加到<script>
:将在DOM完全构建后执行。
- 添加attribute
type="module"
to <script>
: Similar to defer
, but will also make your code be treated as a JS module. This will also make your code run in strict mode.
- 使用 JS event
DOMContentLoaded
:类似于 defer
,但封装在您的 JS-file.
- 使用 JS event
load
:类似于 DOMContentLoaded
,但会额外等待 所有 资源(例如图像、视频)加载完毕。 如果适用,首选DOMContentLoaded
。
- 将
<script>
移至 HTML 的底部:效果与 defer
相同。带有 defer
的脚本仍将在底部的脚本之后加载。
最简单的解决方案是使用 defer
,因为这样您就不必更改 JS 代码:
<script src="main.js" defer></script>
顺便说一下:不要被 Whosebug 的代码片段所愚弄;使用 on-site 片段时,代码的 <script>
被移至 HTML!
的底部
特色!
可变生命周期
JS 中的变量仅在其上下文期间持续存在。 (Closures 会导致他们周围的范围在他们活着的时候一直存在!)
要使变量在调用函数时保持不变,必须在此函数外部声明它:
let someVariable = 0;
function aFunc() {
// `someVariable` will persist across calls!
}
这意味着,必须在您的函数外部声明用于跟踪要显示的经文的变量。
显示和隐藏!
用next-to-show节的索引计算上一节的索引,我们只需要保留一个计数器。有两个计数器,如果我们不正确处理它们,它们可能会不同步。
let nextToShow = 0;
function showNextVerse() {
const previousIndex = nextToShow - 1;
// ...
++nextToShow; // Increase counter for next call
}
在我们的例子中,用户(或者更确切地说,他们的点击)将扮演循环的角色。它们偶尔会导致我们的点击处理程序(函数)运行,这时我们必须交换经文。
可以通过多种方式交换经文,但我们会坚持使用您的“内联样式”方式:(最终代码)
document.addEventListener("click", showNextVerse);
const banner = document.querySelector(".banner");
const verses = document.getElementById("verses").children; // More on this later
let nextToShow = 0;
function showNextVerse() {
const previousIndex = nextToShow - 1;
// On every call, hide the banner
banner.style.display = "none"; // Use `.style` instead of `.innerHTML` to preserve its HTML!
// Hide previous if valid index
if (previousIndex >= 0 && previousIndex < verses.length) {
verses[previousIndex].style.display = "none";
}
// Show next if valid index
if (nextToShow >= 0 && nextToShow < verses.length) {
verses[nextToShow].style.display = "block";
}
++nextToShow;
}
<div class="banner">
<script src="main.js" defer></script>
<img alt="We are still building on our Website:-)">
</div>
<div id="verses">
<div style="display:none">Lorem Ipsum1</div>
<div style="display:none">Lorem Ipsum2</div>
<div style="display:none">Lorem Ipsum3</div>
<div style="display:none">Lorem Ipsum4</div>
<div style="display:none">Lorem Ipsum5</div>
</div>
改进?!
不需要变量versesLength
;您可以直接用 verses.length
替换它的每个出现。它不会改进原始名称,如果不与原始变量同步,它又是一个潜在的错误来源。
正确使用class
和id
目前,您的诗句使用 class
就好像它是 id
;他们每个人都使用不同的class。这没有错,但在语义上我会为此目的使用 id
。
要有效地使用 class
属性,您应该为每一节指定 class verse
。这样,您可以通过 JS 更轻松地 select 它们(请参阅下一节)。
更容易获取元素
与编码世界中的所有事物一样,一个问题有多种解决方案。您解决了以相当繁琐的方式获取元素的问题,但还有其他选择:(Non-exhaustive list)
- 使用
document.querySelectorAll()
.
- 重命名经文以使用相同的 class,并使用
document.getElementsByClassName()
。
- 使用
Element.children
.
您可能已经注意到我是如何获得所有经文的。事实上,verses
(在最后的代码中)甚至没有引用一个数组,而是一个 HTMLCollection
。它与数组非常相似,除了它会根据更改实时更新:
const elementsWrapper = document.getElementById("elements");
const collection = elementsWrapper.children;
const array = Array.from(elementsWrapper.children);
document.getElementById("bt-add").addEventListener("click", function() {
const newElement = document.createElement("div");
newElement.textContent = "Added later";
elementsWrapper.appendChild(newElement);
});
document.getElementById("bt-log").addEventListener("click", function() {
console.log("collection.length:", collection.length);
console.log("array.length:", array.length);
});
<button id="bt-add">Add new element</button>
<button id="bt-log">Log <code>.length</code></button>
<p>
Try logging first! Then try to add elements, and log again! See the difference?
</p>
<div>Elements:</div>
<div id="elements">
<div>Initially existent</div>
<div>Initially existent</div>
</div>
另一种隐藏方式
隐藏元素的方法如下:
- 使用inline styling(这就是你所做的!)。
- 使用 CSS classes.
- 使用 HTML attribute
hidden
.
对于小的样式更改,我也会使用内联样式。但是对于仅隐藏元素,我会使用 hidden
属性。
此外,还有多种CSS隐藏元素的方法:
- 使用
display: none
:将隐藏元素,就好像它不存在一样。
- 使用
opacity: 0
:将元素隐藏起来;它仍然占用 space,并且仍然应该是 accessibility tree 的一部分(自以为是)。
- 使用
position: fixed
和 top
、left
等属性移动它 off-site:(请不要。)
将在视觉上移动元素 off-site。它仍将是可访问性树的一部分,并且仅适用于具有预期书写方向的语言(例如,它不适用于 right-to-left 语言)。
- 将
width
、height
、margin
、padding
和 border
设置为 0:将仅在视觉上隐藏元素;它仍然是可访问性树的一部分,并且ill stop margin collapse. Screen-reader only classes 将此用于 non-visual 元素,非常有用。
我希望文本部分在单击时出现和消失。
第一次点击前只能看到banner,还没有经文;第一次单击时出现第一节,第二次单击时第二节出现在第一节的位置,依此类推。
我试图通过隐藏元素来实现这一点,将它们放在一个数组中,并让它们在调用函数的次数符合诗句的索引时显示。
我是 JavaScript 的新手,不了解抛出的异常。如果我尝试在线检查我的代码,当 O 尝试通过单击网站调用该函数时,我会收到此异常:
Uncaught TypeError: Cannot read properties of null (reading 'style')
到目前为止,这是我的代码:
const text = document.querySelector(".banner")
document.addEventListener('click', myFunction);
const verse1 = document.querySelector(".verse1")
const verse2 = document.querySelector(".verse2")
const verse3 = document.querySelector(".verse3")
const verse4 = document.querySelector(".verse4")
const verse5 = document.querySelector(".verse5")
const verses = [verse1, verse2, verse3, verse4, verse5]
let versesLength = verses.length;
function myFunction() {
for (let i = 0; i < versesLength; i++) {
text.innerHTML = verses[i].style.display = 'block';
}
}
<div class="banner">
<script src="main.js"></script>
<img src="files/SomeLogo.jpg" alt="We are still building on our Website:-)">
</div>
<div id="verses">
<div class="verse1" style="display: none">Lorem Ipsum</div>
<div class="verse2" style="display: none">Lorem Ipsum2</div>
<div class="verse3" style="display: none">Lorem Ipsum3</div>
<div class="verse4" style="display: none">Lorem Ipsum4</div>
<div class="verse5" style="display: none">Lorem Ipsum5</div>
</div>
我被卡住了,在过去的几个小时里点击了类似的问题。在此先感谢您的帮助
这应该可以做到:
const verses = document.querySelectorAll('.verse');
const banner = document.querySelector('.banner');
const length = verses.length;
let counter = 0;
document.onclick = () => {
if (counter === 0) banner.classList.add('hide');
if (counter >= length) return;
verses[counter].classList.add('show');
if (verses[counter - 1]) verses[counter - 1].classList.remove('show');
counter++;
};
body {
background: orange;
}
.hide {
display: none !important;
}
.show {
display: block !important;
}
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<div class="banner">
<img
src="files/SomeLogo.jpg"
alt="We are still building on our Website:-)"
/>
</div>
<div id="verses">
<div class="verse1 verse hide">Lorem Ipsum</div>
<div class="verse2 verse hide">Lorem Ipsum2</div>
<div class="verse3 verse hide">Lorem Ipsum3</div>
<div class="verse4 verse hide">Lorem Ipsum4</div>
<div class="verse5 verse hide">Lorem Ipsum5</div>
</div>
<script src="main.js"></script>
</body>
</html>
无需更改 HTML 中的任何内容,您可以在 javascript
中执行类似的操作const text = document.querySelector(".banner")
document.addEventListener('click', myFunction);
let verses = document.querySelector("#verses").children
let count = 0
function myFunction() {
Array.from(verses).forEach(el=> el.style.display="none")
if(count < verses.length){
verses[count].style.display = 'block'
count ++
if(count===verses.length) count =0
}
}
您可以通过为所有经文元素提供相同的 class:
来抓取它们verse
来消除对数组的需要。我们可以用querySelectorAll
.在每节经文中添加 a data attribute 以识别它们。
为了限制全局变量的数量,我们可以使用闭包 - 在
addEventListener
中,我们调用初始化计数的handleClick
函数,然后 returns 将分配给侦听器的功能。这是a closure。它维护着它返回时可以使用的外部词法环境(即变量)的副本。
// Cache the elements with the verse class
const banner = document.querySelector('.banner');
const verses = document.querySelectorAll('.verse');
// Call `handleClick` and assign the function it
// returns to the listener
document.addEventListener('click', handleClick());
function handleClick() {
// Initialise `count`
let count = 1;
// Return a function that maintains a
// copy of `count`
return function () {
// If the count is 5 or less
if (count < verses.length + 1) {
// Remove the banner
if (count === 1) banner.remove();
// Remove the previous verse
if (count > 1) {
const selector = `[data-id="${count - 1}"]`;
const verse = document.querySelector(selector);
verse.classList.remove('show');
}
// Get the new verse
const selector = `[data-id="${count}"]`;
const verse = document.querySelector(selector);
// And show it
verse.classList.add('show');
// Increase the count
++count;
}
}
}
.verse { display: none; }
.show { display: block; margin-top: 1em; padding: 0.5em; border: 1px solid #787878; }
[data-id="1"] { background-color: #efefef; }
[data-id="2"] { background-color: #dfdfdf; }
[data-id="3"] { background-color: #cfcfcf; }
[data-id="4"] { background-color: #bfbfbf; }
[data-id="5"] { background-color: #afafaf; }
<div class="banner">
<img src="https://dummyimage.com/400x75/404082/ffffff&text=We+are+still+building+our+website" alt="We are still building on our Website:-)">
</div>
<div>
<div data-id="1" class="verse">Lorem Ipsum 1</div>
<div data-id="2" class="verse">Lorem Ipsum 2</div>
<div data-id="3" class="verse">Lorem Ipsum 3</div>
<div data-id="4" class="verse">Lorem Ipsum 4</div>
<div data-id="5" class="verse">Lorem Ipsum 5</div>
</div>
其他文档
修复错误
出现“无法读取 null 的属性”错误是因为您尝试访问 null 的属性。您的数组包含空值,因为您试图在浏览器将元素插入其 DOM.
之前获取这些元素浏览器解析 HTML 的方式与您阅读它的方式相同:从左到右,从上到下。
如果浏览器遇到常规 <script>
元素,它会停止解析并首先执行 JavaScript。当然,某些元素可能在 DOM.
有多种延迟脚本执行的方法:
- 将attribute
defer
添加到<script>
:将在DOM完全构建后执行。 - 添加attribute
type="module"
to<script>
: Similar todefer
, but will also make your code be treated as a JS module. This will also make your code run in strict mode. - 使用 JS event
DOMContentLoaded
:类似于defer
,但封装在您的 JS-file. - 使用 JS event
load
:类似于DOMContentLoaded
,但会额外等待 所有 资源(例如图像、视频)加载完毕。 如果适用,首选DOMContentLoaded
。 - 将
<script>
移至 HTML 的底部:效果与defer
相同。带有defer
的脚本仍将在底部的脚本之后加载。
最简单的解决方案是使用 defer
,因为这样您就不必更改 JS 代码:
<script src="main.js" defer></script>
顺便说一下:不要被 Whosebug 的代码片段所愚弄;使用 on-site 片段时,代码的 <script>
被移至 HTML!
特色!
可变生命周期
JS 中的变量仅在其上下文期间持续存在。 (Closures 会导致他们周围的范围在他们活着的时候一直存在!)
要使变量在调用函数时保持不变,必须在此函数外部声明它:
let someVariable = 0;
function aFunc() {
// `someVariable` will persist across calls!
}
这意味着,必须在您的函数外部声明用于跟踪要显示的经文的变量。
显示和隐藏!
用next-to-show节的索引计算上一节的索引,我们只需要保留一个计数器。有两个计数器,如果我们不正确处理它们,它们可能会不同步。
let nextToShow = 0;
function showNextVerse() {
const previousIndex = nextToShow - 1;
// ...
++nextToShow; // Increase counter for next call
}
在我们的例子中,用户(或者更确切地说,他们的点击)将扮演循环的角色。它们偶尔会导致我们的点击处理程序(函数)运行,这时我们必须交换经文。
可以通过多种方式交换经文,但我们会坚持使用您的“内联样式”方式:(最终代码)
document.addEventListener("click", showNextVerse);
const banner = document.querySelector(".banner");
const verses = document.getElementById("verses").children; // More on this later
let nextToShow = 0;
function showNextVerse() {
const previousIndex = nextToShow - 1;
// On every call, hide the banner
banner.style.display = "none"; // Use `.style` instead of `.innerHTML` to preserve its HTML!
// Hide previous if valid index
if (previousIndex >= 0 && previousIndex < verses.length) {
verses[previousIndex].style.display = "none";
}
// Show next if valid index
if (nextToShow >= 0 && nextToShow < verses.length) {
verses[nextToShow].style.display = "block";
}
++nextToShow;
}
<div class="banner">
<script src="main.js" defer></script>
<img alt="We are still building on our Website:-)">
</div>
<div id="verses">
<div style="display:none">Lorem Ipsum1</div>
<div style="display:none">Lorem Ipsum2</div>
<div style="display:none">Lorem Ipsum3</div>
<div style="display:none">Lorem Ipsum4</div>
<div style="display:none">Lorem Ipsum5</div>
</div>
改进?!
不需要变量versesLength
;您可以直接用 verses.length
替换它的每个出现。它不会改进原始名称,如果不与原始变量同步,它又是一个潜在的错误来源。
正确使用class
和id
目前,您的诗句使用 class
就好像它是 id
;他们每个人都使用不同的class。这没有错,但在语义上我会为此目的使用 id
。
要有效地使用 class
属性,您应该为每一节指定 class verse
。这样,您可以通过 JS 更轻松地 select 它们(请参阅下一节)。
更容易获取元素
与编码世界中的所有事物一样,一个问题有多种解决方案。您解决了以相当繁琐的方式获取元素的问题,但还有其他选择:(Non-exhaustive list)
- 使用
document.querySelectorAll()
. - 重命名经文以使用相同的 class,并使用
document.getElementsByClassName()
。 - 使用
Element.children
.
您可能已经注意到我是如何获得所有经文的。事实上,verses
(在最后的代码中)甚至没有引用一个数组,而是一个 HTMLCollection
。它与数组非常相似,除了它会根据更改实时更新:
const elementsWrapper = document.getElementById("elements");
const collection = elementsWrapper.children;
const array = Array.from(elementsWrapper.children);
document.getElementById("bt-add").addEventListener("click", function() {
const newElement = document.createElement("div");
newElement.textContent = "Added later";
elementsWrapper.appendChild(newElement);
});
document.getElementById("bt-log").addEventListener("click", function() {
console.log("collection.length:", collection.length);
console.log("array.length:", array.length);
});
<button id="bt-add">Add new element</button>
<button id="bt-log">Log <code>.length</code></button>
<p>
Try logging first! Then try to add elements, and log again! See the difference?
</p>
<div>Elements:</div>
<div id="elements">
<div>Initially existent</div>
<div>Initially existent</div>
</div>
另一种隐藏方式
隐藏元素的方法如下:
- 使用inline styling(这就是你所做的!)。
- 使用 CSS classes.
- 使用 HTML attribute
hidden
.
对于小的样式更改,我也会使用内联样式。但是对于仅隐藏元素,我会使用 hidden
属性。
此外,还有多种CSS隐藏元素的方法:
- 使用
display: none
:将隐藏元素,就好像它不存在一样。 - 使用
opacity: 0
:将元素隐藏起来;它仍然占用 space,并且仍然应该是 accessibility tree 的一部分(自以为是)。 - 使用
position: fixed
和top
、left
等属性移动它 off-site:(请不要。)
将在视觉上移动元素 off-site。它仍将是可访问性树的一部分,并且仅适用于具有预期书写方向的语言(例如,它不适用于 right-to-left 语言)。 - 将
width
、height
、margin
、padding
和border
设置为 0:将仅在视觉上隐藏元素;它仍然是可访问性树的一部分,并且ill stop margin collapse. Screen-reader only classes 将此用于 non-visual 元素,非常有用。