仅检测伪元素上的点击事件

Posted

技术标签:

【中文标题】仅检测伪元素上的点击事件【英文标题】:Only detect click event on pseudo-element 【发布时间】:2020-09-19 06:26:07 【问题描述】:

请看这个小提琴:http://jsfiddle.net/ZWw3Z/5/

我的代码是:

p 
    position: relative;
    background-color: blue;


p:before 
    content: '';
    position: absolute;
    left:100%;
    width: 10px;
    height: 100%;
    background-color: red;
    <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate...</p>

我只想在伪元素(红色位)上触发点击事件。也就是我不希望蓝色位上触发点击事件。

【问题讨论】:

检查点击的位置怎么样? ***.com/questions/3234977/… 请阅读我之前发布的解决方案here。 可能是一个很好的解决方法HERE。 试试$('p').before().click(function...) @RezaMamun 你能检查一下并建议我如何实现它。 ***.com/questions/66923436/… 【参考方案1】:

这是不可能的;伪元素根本不是 DOM 的一部分,因此您不能直接将任何事件绑定到它们,您只能绑定到它们的父元素。

如果你必须只在红色区域有一个点击处理程序,你必须创建一个子元素,比如span,将它放在打开的<p>标签之后,将样式应用于p span而不是@ 987654325@,并绑定到它。

【讨论】:

似乎在现代浏览器中,元素本身及其伪元素的 pointer-events 值不同:jsfiddle.net/ZWw3Z/70 @Ilya Streltsyn 太棒了 - 不是“正确”答案(如果您还需要单击该元素,则无法使用),但对于 div 上的“关闭按钮”非常有效 根据 Ilya 发布的 jsfiddel,pointer-events 似乎对 IE9 没有作用。 @user393274:IE9 不支持 SVG 之外的指针事件。 @theyuv 不,在该页面上,整行都是可点击的,无论是在链接的左侧还是右侧。只有链接本身有不同的点击处理程序,所以点击它不会触发折叠/展开子树。【参考方案2】:

其实是可以的。您可以检查单击的位置是否在元素之外,因为只有在单击 ::before::after 时才会发生这种情况。

此示例仅检查右侧的元素,但这应该适用于您的情况。

span = document.querySelector('span');

span.addEventListener('click', function (e) 
    if (e.offsetX > span.offsetWidth) 
        span.className = 'c2';
     else 
        span.className = 'c1';
    
);
div  margin: 20px; 
span:after  content: 'AFTER'; position: absolute; 

span.c1  background: yellow; 
span.c2:after  background: yellow; 
<div><span>ELEMENT</span></div>

JSFiddle

【讨论】:

它对我不起作用,当我点击任何一个时,文本都会突出显示。使用 Firefox,如果重要的话(不应该)。 对于火狐:jsfiddle.net/wC2p7/16。如果使用嵌套元素,您可能需要根据需要计算嵌套偏移量(例如 jQuery: $(e.target).offset().left ) 太棒了,谢谢,它有效。但在 Firefox 和其他符合标准的浏览器中,如果要测试鼠标是否悬停在 :after 上,您必须检查 if (e.clientX > span.offsetLeft + span.offsetHeight),因为这些浏览器没有 e.offsetX 属性。小提琴:jsfiddle.net/Noitidart/wC2p7/18 如果 jQuery 决定支持 $('a:after').on('click', fn) 会更酷,但我并没有真正看到这种情况发生:p 如果::before::after 位于元素内部,这实际上不起作用。【参考方案3】:

这些答案都不可靠,我的答案也不会更可靠。

除此之外,如果您确实遇到了您尝试单击的元素没有填充的幸运场景(这样元素的所有“内部”空间都被子元素完全覆盖) ,然后您可以根据容器本身检查单击事件的目标。如果匹配,则表示您点击了 :before 或 :after 元素。

显然这对于​​这两种类型(之前和之后)都不可行,但是我已将其实现为 hack/speed 修复,并且它运行良好,没有一堆位置检查,这可能不准确,具体取决于百万个不同的因素。

【讨论】:

【参考方案4】:

我的回答适用于任何想要点击页面确定区域的人。这对我的 绝对定位 :after

有用

感谢article,我意识到(使用 jQuery)我可以使用 e.pageYe.pageX 而不必担心浏览器之间的 e.offsetY/Xe.clientY/X 问题。

通过反复试验,我开始在 jQuery 事件对象中使用 clientX 和 clientY 鼠标坐标。这些坐标为我提供了鼠标相对于浏览器视口左上角的 X 和 Y 偏移量。然而,当我阅读 Karl Swedberg 和 Jonathan Chaffer 撰写的 jQuery 1.4 参考指南时,我看到他们经常提到 pageX 和 pageY 坐标。在查看更新的 jQuery 文档后,我看到这些是 jQuery 标准化的坐标;而且,我看到他们给了我鼠标相对于整个文档(不仅仅是视口)的 X 和 Y 偏移量。

我喜欢这个event.pageY 的想法,因为它总是相同的,因为它与文档相关。我可以使用 offset() 将它与我的 :after 父元素进行比较,它也返回相对于文档的 X 和 Y。

因此,我可以在整个页面上想出一个永不改变的“可点击”区域。


这是我的demo on codepen。


或者如果懒得用 codepen,这里是 JS:

* 对于我的示例,我只关心 Y 值。

var box = $('.box');
// clickable range - never changes
var max = box.offset().top + box.outerHeight();
var min = max - 30; // 30 is the height of the :after

var checkRange = function(y) 
  return (y >= min && y <= max);


box.click(function(e)
  if ( checkRange(e.pageY) ) 
    // do click action
    box.toggleClass('toggle');
  
);

【讨论】:

【参考方案5】:

现代浏览器上,您可以尝试使用 pointer-events css 属性(但这会导致无法检测到父节点上的鼠标事件):

p 
    position: relative;
    background-color: blue;
    color:#ffffff;
    padding:0px 10px;
    pointer-events:none;

p::before 
    content: attr(data-before);
    margin-left:-10px;
    margin-right:10px;
    position: relative;
    background-color: red;
    padding:0px 10px;
    pointer-events:auto;

当事件目标是你的“p”元素时,你就知道它是你的“p:before”。

如果您仍然需要检测主 p 上的鼠标事件,您可以考虑修改 html 结构的可能性。您可以添加 span 标记和以下样式:

p span 
    background:#393;
    padding:0px 10px;
    pointer-events:auto;

事件目标现在是“span”和“p:before”元素。

不带 jquery 的示例:http://jsfiddle.net/2nsptvcu/

jquery 示例:http://jsfiddle.net/0vygmnnb/

这里是支持指针事件的浏览器列表:http://caniuse.com/#feat=pointer-events

【讨论】:

我不知道为什么是父节点。你是说如果我在 .box:before 之类的点击事件,那么 .box 就无法检测到任何点击? 部分正确:如果您的 CSS 包含类似 .boxpointer-events:none; .box:beforepointer-events:auto; 的内容,那么唯一仍然支持事件的元素是 p :前。无论如何请记住,js 会返回主 .box 元素,因为 .box:before 并不真正存在于 DOM 中。 您实际上仍然可以在原始元素上检测鼠标事件。你不需要总是设置这个规则,你可以简单地设置它的时间来检查元素是否仍然悬停,即使只有它的伪元素确实接收指针事件:jsfiddle.net/0z8p5ret这将导致一个很大的回流次数,所以任何其他方式可能会更好。【参考方案6】:

在 Click 事件中添加条件来限制可点击区域。

    $('#thing').click(function(e) 
       if (e.clientX > $(this).offset().left + 90 &&
             e.clientY < $(this).offset().top + 10) 
                 // action when clicking on after-element
                 // your code here
       
     );

DEMO

【讨论】:

【参考方案7】:

这对我有用:

$('#element').click(function (e) 
        if (e.offsetX > e.target.offsetLeft) 
            // click on element
        
         else
           // click on ::before element
       
);

【讨论】:

【参考方案8】:

不,但你可以这样做

在 html 文件中添加此部分

<div class="arrow">
</div>

在css中你可以这样做

p div.arrow 
    content: '';
    position: absolute;
    left:100%;
    width: 10px;
    height: 100%;
    background-color: red;
 

希望对你有帮助

【讨论】:

【参考方案9】:

简答:

我做到了。 我为那里的所有小人物写了一个动态使用的函数......

Working example which displays on the page

Working example logging to the console

长答案:

...还是做了。

我花了一段时间才完成,因为页面上并没有真正的伪元素。虽然上面的一些答案在某些情况下有效,但它们都不能既是动态的,也不能在元素的大小和位置都出乎意料的情况下工作(例如绝对定位的元素覆盖父元素的一部分)。我的没有。

用法:

//some element selector and a click event...plain js works here too
$("div").click(function() 
    //returns an object before: true/false, after: true/false
    psuedoClick(this);

    //returns true/false
    psuedoClick(this).before;

    //returns true/false
    psuedoClick(this).after;

);

工作原理:

它抓取父元素的高、宽、上、左位置(基于远离窗口边缘的位置),并抓取高、宽、上、左位置(基于边缘父容器)并比较这些值以确定伪元素在屏幕上的位置。

然后它比较鼠标的位置。只要鼠标在新创建的变量范围内,它就会返回true。

注意:

让父元素 RELATIVE 定位是明智的。如果您有一个绝对定位的伪元素,则此功能仅在它基于父级的尺寸定位时才有效(因此父级必须是相对的……也许粘性或固定也可以……我不知道)。

代码:

function psuedoClick(parentElem) 

    var beforeClicked,
      afterClicked;

  var parentLeft = parseInt(parentElem.getBoundingClientRect().left, 10),
      parentTop = parseInt(parentElem.getBoundingClientRect().top, 10);

  var parentWidth = parseInt(window.getComputedStyle(parentElem).width, 10),
      parentHeight = parseInt(window.getComputedStyle(parentElem).height, 10);

  var before = window.getComputedStyle(parentElem, ':before');

  var beforeStart = parentLeft + (parseInt(before.getPropertyValue("left"), 10)),
      beforeEnd = beforeStart + parseInt(before.width, 10);

  var beforeYStart = parentTop + (parseInt(before.getPropertyValue("top"), 10)),
      beforeYEnd = beforeYStart + parseInt(before.height, 10);

  var after = window.getComputedStyle(parentElem, ':after');

  var afterStart = parentLeft + (parseInt(after.getPropertyValue("left"), 10)),
      afterEnd = afterStart + parseInt(after.width, 10);

  var afterYStart = parentTop + (parseInt(after.getPropertyValue("top"), 10)),
      afterYEnd = afterYStart + parseInt(after.height, 10);

  var mouseX = event.clientX,
      mouseY = event.clientY;

  beforeClicked = (mouseX >= beforeStart && mouseX <= beforeEnd && mouseY >= beforeYStart && mouseY <= beforeYEnd ? true : false);

  afterClicked = (mouseX >= afterStart && mouseX <= afterEnd && mouseY >= afterYStart && mouseY <= afterYEnd ? true : false);

  return 
    "before" : beforeClicked,
    "after"  : afterClicked

  ;      


支持:

我不知道....看起来 ie 很笨,有时喜欢将 auto 作为计算值返回。如果尺寸在 CSS 中设置,它似乎在所有浏览器中都能正常工作。所以......在你的伪元素上设置你的高度和宽度,并且只在顶部和左侧移动它们。我建议将它用于您认为它不起作用的事情上。比如动画什么的。 Chrome 可以正常工作。

【讨论】:

event 每次触发事件时都会通过事件处理函数传递。比如点击或打字。当我们使用.click()函数时,我们正在等待点击事件。我们可以指定并说.click(function(e)...) 以使事件成为e,但也可以只使用event psEUdo,不是 psUEdo! 上面的代码不起作用,因为event是未定义的。 @SebastianZartner - 事件是一个关键字。这是当用户做某事的时候。我在您的评论上方逐字解释了这两个 cmets。当用户与页面交互时,会触发一个事件(在大多数情况下)。 Event 是来自事件侦听器“.click()”的关键字。如果开发人员想为事件使用不同的名称,他们可以在事件侦听器中设置它,例如“.click(e)”,这是一种更常见的查看方式。但是....即使我在上面有一个链接,我也会包含一个更明显的链接,它不会登录到控制台,而是登录到有需要的页面。 我应该提到它在 Firefox 中不起作用,因为 event 是未定义的。不过,它适用于 Chrome 和 Edge。【参考方案10】:

这是 Fasoeu 使用最新的 CSS3 和 JS ES6 编辑的答案

Edited demo 不使用 JQuery。

最短的代码示例:

<p><span>Some text</span></p>
p 
    position: relative;
    pointer-events: none;

p::before 
    content: "";
    position: absolute;
    pointer-events: auto;

p span 
    display: contents;
    pointer-events: auto;

const all_p = Array.from(document.querySelectorAll('p'));

for (let p of all_p) 
    p.addEventListener("click", listener, false);
;

说明:

pointer-events 控制事件检测,从目标中删除接收事件,但继续从伪元素接收使得点击::before::after 成为可能,您将始终知道您在伪元素上点击了什么,但是,如果您仍然需要单击,则将所有内容放在嵌套元素中(例如span),但是由于我们不想应用任何其他样式,display: contents; 成为非常方便的解决方案,并且由most browsers 支持. pointer-events: none; 正如在原帖中已经提到的,也广泛应用于supported。

javascript 部分也使用了广泛支持的Array.fromfor...of,但是它们不是必须在代码中使用。

【讨论】:

【参考方案11】:

我通过在 :after css 处添加 pointer-events: none; 解决了这个问题

【讨论】:

你能解释更多吗?目前多个其他答案使用相同的解决方案。那么是什么让您的答案为其他用户添加了一些东西?请编辑您的答案:) 这个解决方案非常棒,因为它依赖于支持 pointer-events 的浏览器的标准 CSS 属性。例如,p pointer-events: none; p::before pointer-events: auto;

以上是关于仅检测伪元素上的点击事件的主要内容,如果未能解决你的问题,请参考以下文章

模拟反应元素上的点击事件

仅捕获不是由锚点点击引起的 hashchange 事件

DOM 只注册一键点击事件移除元素

根据条件排除点击事件?

如何通过叠加层检测谷歌地图上的点击事件?

css下层元素阻止了上层元素的点击事件,如何解决?