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


212

我的代码是:

p {
    position: relative;
    background-color: blue;
}

p:before {
    content: '';
    position: absolute;
    left:100%;
    width: 10px;
    height: 100%;
    background-color: red;
}

请查看此小提琴:http : //jsfiddle.net/ZWw3Z/5/

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


1
如何检查点击的位置? stackoverflow.com/questions/3234977/...

请在这里阅读我以前发布的解决方案。
Amr

可能是一个很好的办法解决这里
Reza Mamun

Answers:


218

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

如果仅在红色区域上必须具有单击处理程序,则必须制作一个子元素(如a)span,将其放置在开始<p>标记之后,将样式应用于p span而不是p:before并绑定到它。


107
似乎在现代浏览器中pointer-events,对于元素本身及其伪元素使用不同的值是可能的:jsfiddle.net/ZWw3Z/70
Ilya Streltsyn

5
@Ilya Streltsyn令人惊叹-不是“正确”的答案(如果您还需要单击元素,则无法使用),但在div上为“关闭按钮”
做出了出色的贡献

pointer-events根据Ilya发布的jsfiddel,似乎没有为IE9达到目的。
2015年

@ user393274:IE9不支持SVG之外的指针事件。
BoltClock

1
@theyuv不,在该页面的整个行都是可单击的,无论在链接的左侧还是右侧。只有链接本身具有不同的单击处理程序,因此单击它不会触发折叠/展开子树。
Ilya Streltsyn

131

实际上,这是可能的。您可以检查单击的位置是否在元素外部,因为只有在单击::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


1
它对我不起作用,当我单击其中任何一个时,文本都会突出显示。使用Firefox(如果不重要)。
2014年

2
对于Firefox:jsfiddle.net/wC2p7/16。如果使用嵌套元素,则可能需要适当地计算嵌套偏移量(例如jQuery:$(e.target).offset()。left)
2014年

1
太棒了,谢谢,它有效。但是在Firefox和其他符合标准的浏览器中,由于这些浏览器不具有该属性,:after因此必须检查鼠标是否经过测试。小提琴:jsfiddle.net/Noitidart/wC2p7/18if (e.clientX > span.offsetLeft + span.offsetHeight)e.offsetX
Noitidart 2014年

5
如果jQuery决定支持,甚至更酷,$('a:after').on('click', fn)但我真的没有看到这种情况:p
LinusUnnebäck2014年

25
如果::before::after位于元素内部,则实际上不起作用。
laconbass

74

现代浏览器上,您可以尝试使用指标事件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之类的click事件,那么.box无法检测到任何点击?
最受尊敬的先生

这部分是正确的:如果您的CSS包含.box {pointer-events:none;} .box:before {pointer-events:auto;}之类的东西,那么唯一仍支持事件的元素就是p:before。无论如何,请记住,由于.box:before并不真正存在于DOM中,因此js会带给您主要的.box元素。
Fasoeu

9

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

由于这个文章,我才意识到(用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。

因此,我可以在整个页面上提出一个范围范围内的“可点击”区域,该区域不会改变。


这是我在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');
  }
});

6

这对我有用:

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

6

简短答案:

我做的。我写了一个函数,供所有小家伙动态使用。

显示在页面上的工作示例

工作示例记录到控制台

长答案:

...还是做到了。

我花了一些时间来做,因为psuedo元素实际上不在页面上。尽管上述某些答案在某些情况下可行,但它们都不能同时发挥作用,并且不能在元素的大小和位置都超出预期的情况下起作用(例如,绝对位置的元素覆盖父元素的一部分)。我的不是。

用法:

//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;

});

这个怎么运作:

它获取父元素的高度,宽度,顶部和左侧位置(基于远离窗口边缘的位置),并获取父元素的高度,宽度,顶部和左侧位置(基于父容器的边缘) ),然后比较这些值以确定psuedo元素在屏幕上的位置。

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

注意:

使父元素相对定位是明智的。如果您有一个绝对定位的psuedo元素,则此函数仅在基于父对象的尺寸进行定位时才起作用(因此,父对象必须是相对的...也许是粘性的或固定的也可以工作....我不知道)。

码:

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

  };      

}

支持:

我不知道....看起来像是愚蠢的,有时喜欢将auto作为计算值返回。如果在CSS中设置了尺寸,则它在所有浏览器中都能正常工作。所以...在psuedo元素上设置高度和宽度,并只在顶部和左侧移动它们。我建议您在无法解决的问题上使用它。像动画之类的。Chrome可以正常运作。


event每次触发事件时都会通过事件处理函数传递。就像单击或键入。使用该.click()功能时,我们正在等待click事件。我们可以指定并说.click(function(e){...})使事件成为,e但仅使用也是可以接受的event

3
psEUdo,而不是psUEdo!
biziclop

上面的代码不起作用,因为event未定义。
塞巴斯蒂安·扎特纳

@SebastianZartner-事件是关键字。这是用户执行某项操作的时间。我从字面上解释了这两个评论。当用户与页面交互时,会触发一个事件(在大多数情况下)。事件是来自事件侦听器“ .click()”的关键字。如果开发人员想要为事件使用其他名称,则可以在事件侦听器中将其设置为“ .click(e)”,这是一种更常见的查看方式。但是....即使我上面有一个链接,我也会包括一个更明显的链接,它不会登录到控制台,而是登录到有需要的页面。

我应该提到它在Firefox中不起作用,因为它event是未定义的。不过,它可以在Chrome和Edge中使用。
塞巴斯蒂安·扎特纳

2

在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
       }
     });

演示


1

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

不使用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;成为非常方便的解决方案,并且大多数浏览器都支持它。pointer-events: none;如在原始帖子中已经提到的,也得到了广泛支持

JavaScript部分也得到了广泛的支持,Array.from并且for...of在代码中不是必需的。


0

这些答案都不是可靠的,而我的答案将更可靠。

抛开警告,如果您遇到了一种幸运的情况,即您要单击的元素没有填充(这样子元素的所有“内部”空间都被子元素完全覆盖了),那么您可以针对容器本身检查click事件的目标。如果匹配,则表示您单击了:before或:after元素。

显然,这两种类型(之前和之后)都不可行,但是我已将其实现为破解/速度修复,并且运行良好,无需进行大量位置检查,这可能不准确,具体取决于大约一百万个不同的因素。


-2

不,但是你可以这样

在html文件中添加此部分

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

在CSS中,你可以这样

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

希望它能对您有帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.