这是一支带有完整html的笔: https://codepen.io/froggomad/pen/WLdzoB
我正在编写2个函数-一个显示隐藏的内容,另一个隐藏它。我希望show()函数在父div上执行,并且hide()函数在使用选择器.click-text
的div上执行。
但是,我将.click-text
上的文本从显示切换为隐藏,所以我不希望隐藏功能始终保留在文本上。我也很希望它的交互文本在更改为隐藏功能时很明显,因此我将其设为链接。
这很好,但是当尝试将父级的onclick
属性设置回show()
函数时,hide块中的任何内容都不会执行。
如果删除设置父级onclick
Attr的行,则脚本将按预期执行。如果我设置了另一个元素的onclick属性,脚本将按预期执行。
但是,在该行中没有任何反应,并且控制台中也没有输出指示错误。我什至设置了元素类型和类名警报,以确保我定位到正确的元素。
获取元素匹配选择器的最接近的父级:
var getClosest = function (element, selector) {
for ( ; element && element !== document; element = element.parentNode ) {
if ( element.matches(selector) ) return element;
}
return null;
}
显示隐藏的元素ul.service-category-menu
function show(elem) {
var menu = elem.querySelector("ul.service-category-menu"),
click = elem.querySelector(".click-text"),
parent = getClosest(elem, '.service-category');
;
if (menu.style.display === "none" || menu.style.display === "") {
menu.style.display = "block";
click.innerHTML = "<a href=\"#\">Click to Hide<\/a>";
click.setAttribute('onclick','hide(this);');
elem.setAttribute('onclick', 'null');
}
}
隐藏元素
function hide(elem) {
var parent = getClosest(elem, '.service-category'),
menu = parent.querySelector("ul.service-category-menu"),
click = parent.querySelector(".click-text")
;
alert(parent + "\n" + parent.className);
//Outputs div element with expected class name (class name is unique on each div)
if (menu.style.display === "block") {
menu.style.display = "none";
click.innerHTML = "Click to Show";
click.setAttribute('onclick', 'null');
//the above lines don't execute when the following line is in place. There's no error in console.
parent.setAttribute('onclick','show(this)');
}
}
答案 0 :(得分:1)
它被执行,它是在您单击Click to Hide
之后,事件继续到父级,并且父级的事件处理程序被执行。因此,实际发生的是(在该行中)在调用hide()
之后,您无意中调用了show()
。
在javascript中,通常将其称为气泡(当您点击子项时,父项的点击处理程序也会在子项的点击处理程序完成后执行)。
因此,您可以在hide()
函数的末尾添加此行
event.stopPropagation();
要阻止事件继续进行到父项
答案 1 :(得分:1)
如其他答案中所述设置event.stopPropagation
可能会解决您的问题。另外,您可以将hide
函数的最后一行更改为window.setTimeout(e => parent.setAttribute('onclick','show(this)'), 0)
。
现在正在发生的事情是:
hide
函数,并在该函数期间将click事件绑定到父对象。通过使用setTimeout(fn, 0)
,可以确保click事件在函数绑定到父级之前已经完成。
答案 2 :(得分:1)
首先,我必须承认我反对使用onclick
属性。如果您不使用VueJS或React之类的框架,则我认为HTML和JS应该保持分离,以实现更好的控制和可维护性。
您可以使用addEventListener
,removeEventListener
和e.stopPropagation()
来避免触发多个事件处理程序。
事件分为两个阶段:
事件捕获 :事件从document
一直传播到目标元素。
要在此阶段捕获事件,请执行以下操作:
elm.addEventListener('click', myFunc, true);
事件冒泡 :事件从目标反弹回到document
。
要在此阶段捕获事件,请执行以下操作:
elm.addEventListener('click', myFunc, false); /* or just omit the 3rd param */
使用e.stopPropagation()
可以中断该链。
// When the DOM is ready
window.addEventListener("DOMContentLoaded", init);
function init() {
// Get all categories
var $categories = document.querySelectorAll(".service-category");
// For each of them
Array.from($categories).forEach(function($category) {
// Add an event listener for clicks
$category.addEventListener("click", show);
});
}
function getClosest(element, selector) {
for (; element && element !== document; element = element.parentNode) {
if (element.matches(selector)) return element;
}
return null;
}
function show(e) {
var $menu = this.querySelector("ul.service-category-menu"),
$click = this.querySelector(".click-text");
if (["none", ""].includes($menu.style.display)) {
$menu.style.display = "block";
$click.innerHTML = '<a href="#">Click to Hide</a>';
$click.addEventListener("click", hide);
// Remove the `show` event listener
this.removeEventListener("click", show);
}
e.stopPropagation();
}
function hide(e) {
var $parent = getClosest(this, ".service-category"),
$menu = $parent.querySelector("ul.service-category-menu"),
$click = $parent.querySelector(".click-text");
if (!["none", ""].includes($menu.style.display)) {
$menu.style.display = "none";
$click.innerHTML = "Click to Show";
$click.removeEventListener("click", hide);
$parent.addEventListener("click", show);
}
e.stopPropagation();
}
.service-category{display:inline-block;border:3px solid #ccc;margin:1%;font-weight:700;font-size:3.5vw;cursor:pointer;background-color:#fff;z-index:3;background-position:center;background-size:cover;color:#000}.click-text{text-align:right;font-size:1.25vw;font-style:italic;font-weight:700;padding-right:1%}.service-category:hover .click-text{color:#b22222}.service-category-menu{display:none;margin-left:8%;margin-right:8%;margin-top:1%;background-color:#fff;font-weight:700;font-size:1.6vw;border-radius:10px}
<div class="service-category web-back" id="web-back">
<div class="row-overlay">
Web <br /> Development
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div>
<div class="service-category web-front" id="web-front">
<div class="row-overlay">
Web <br /> Design
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div>