我正在尝试使某些“卡片”彼此相邻显示,并且当单击卡片时,我想要获得卡片的ID。但是,除非单击卡div的边框,否则我的函数将在单击时获得未定义的子元素的ID,而不是父卡的ID。我如何确定它具体获得了卡的ID?
这是HTML:
<div id="parentCard" onclick="getID(event)">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
Javascript:
function getID(event){
console.log(event.target.id);
}
答案 0 :(得分:3)
获取事件的目标元素(单击的元素)。
这里,我使用destructuring从目标中获取id
,tagName
和parentNode
属性。
如果单击的元素是card div元素,请记录ID,否则记录父元素的ID。
注意:这假设卡片的嵌套HTML不会比您示例中的嵌套嵌套深,或者parentNode
不会应用于正确的父元素。
const parent = document.getElementById('parentCard');
parent.addEventListener('click', handleClick, false);
function handleClick(e) {
const { id, tagName, parentNode } = e.target;
console.log(tagName === 'DIV' ? id : parentNode.id);
}
<div id="parentCard">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
答案 1 :(得分:2)
尝试使用Event.currentTarget,其中
标识事件的当前目标,因为事件遍历DOM。它始终是指事件处理程序已附加到的元素,而不是
event.target
,它标识发生事件的元素。
function getID(event){
console.log(event.currentTarget.id);
}
<div id="parentCard" onclick="getID(event)">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
答案 2 :(得分:2)
每次用户单击页面上的某处时,用户代理都会确定单击了哪个“非复合”元素,并触发以“ that ”元素为事件的“ click”事件类型的事件目标。
此处的“非复合”元素表示不包含后代元素的元素(例如,可能具有后代 nodes ,例如文本)。
由于您的卡片通常是复合元素-包含其他元素(可能又包含其他元素),因此无法保证点击事件target
属于这些元素。
用户代理是一台机器,无法为您考虑,也无法知道用户“单击卡”。如果卡片中包含按钮,链接或图像,则用户代理所要做的就是通过事件的target
属性告知您用户单击了哪个按钮-您需要确定一个事实,即从您的角度来看,有人点击了卡片。
至少有两种方法可以确定所述事实:
找出哪个卡元素是或包含被点击的元素:
function get_card(element) {
for(; element; element = element.parentElement) {
if(is_card(element)) return element;
}
}
遍历祖先列表,您如何知道某个元素是一张牌?就文档对象模型而言,它只是一个div
元素,具有一个onclick
属性和一个ID。您的文档中可能有div
个元素,它们具有已定义的id
和onclick
属性,而不是卡片。
向元素添加类是对它进行分类的自然方法:
<div class="card">
<!-- descendant elements -->
</div>
通过上述内容,我们决定并假设每个卡片元素的类名列表中都应包含“卡片”。现在,您可以编写is_card
函数(用于get_card
),如下所示:
function is_card(element) {
return element.classList.contains("card");
}
使用程序中的get_card(element)
功能,您现在可以可靠地回答问题“哪张卡包含element
?”。结合使用window
对象上的单个事件侦听器,您现在可以知道用户单击了哪张卡:
addEventListener("click", function(ev) {
var card = get_card(ev.target);
if(card) {
/// A card was clicked, do something about it.
}
}
如您所见,您甚至不需要在每张卡上添加侦听器,只需将一个添加到一个容器元素(如上面的情况,即window
),就可以使用此解决方案
现在,这是所谓的线性时间算法的一种情况-您的卡(“后代”元素依次包含其他后代元素)越“深”,定位该卡所需的平均时间就越长后代元素。但是,如果允许我在此处通用,则无需担心“点击”处理和现代JavaScript运行时的性能。
对card的孩子使用[到目前为止的实验] CSS4和pointer-events: none
规则。假设每个卡都包含“ card”类,则可以指定没有任何后代元素启用指针事件-这些绝不会触发“ click”事件,而card元素将改为:
.card > * {
pointer-events: none;
}
对pointer-events
的支持仍处于试验阶段,但我知道最近的Google Chrome和Firefox都支持它。不过,您在这里要特别小心-例如,您是否准备禁用某些卡片元素的交互性?如果那里有通常是交互式的表单元素怎么办?您需要自己评估这些要求。
有第三个解决方案,但我认为它至少不及第一个解决方案。为了完整起见,在每张卡片上分配事件侦听器可能很浪费,尤其是在每页上有很多卡片的情况下。但是,无论如何,如果您像您自己那样将“ click”侦听器附加到卡上,则事件类型“ click”的侦听器将添加到该元素,该属性可以通过ev.currentTarget
属性使用!意味着无论哪个后代元素触发“ click”事件,该事件都会通过其capture和bubbling阶段到达card元素,并且currentTarget
属性值将始终引用侦听器添加到的元素-在这种情况下是card元素。当然,此解决方案意味着将侦听器附加到每张卡上。