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

请看这把小提琴: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>

I would like to trigger a click event only on the pseudo-element (the red bit). That is, I don't want the click event to be triggered on the blue bit.

327891 次浏览

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

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

事实上,这是可能的。你可以检查被点击的位置是否在元素之外,因为这只会在::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

这些答案都不可靠,我的答案也不可靠得多。

除了注意事项外,如果您确实遇到了这样一种幸运的情况,即您试图单击的元素没有填充(这样元素的所有“内部”空间都被子元素完全覆盖),那么您可以根据容器本身检查click事件的目标。如果匹配,这意味着您单击了:before或:after元素。

显然,这两种类型(之前和之后)都不可行,但我已经实现了它作为一个hack/速度修复,它工作得很好,没有一堆位置检查,这可能是不准确的,取决于大约一百万个不同的因素。

我的答案将适用于任何想要点击页面特定区域的人。这适用于我的绝对定位:在

多亏了这个文章,我意识到(使用jQuery)我可以使用e.pageYe.pageX,而不是担心浏览器之间的e.offsetY/Xe.clientY/X问题。

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

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

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


这是我的codeen上的演示


或者如果懒得用codeen,下面是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');
}
});

现代浏览器上,您可以尝试使用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结构。你可以添加跨度标签和以下样式:

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

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

不使用jquery的例子

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

下面是支持指针事件的浏览器列表

在单击事件中添加条件以限制可单击区域。

    $('#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

这对我来说很管用:

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

不,但你可以这样做

在html文件中添加此部分

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

在css中你可以这样做

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

希望对你有所帮助

简短的回答:

我做到了。 我为所有的小人物写了一个动态使用的函数…< / p >

页面上显示的工作示例

登录控制台的工作示例

长一点的回答:

...还是做到了。

这花了我一段时间来做,因为伪元素不是真的在页面上。虽然上面的一些答案在某些场景中是有效的,但它们都不是动态的,并且在元素的大小和位置都是意外的场景中都有效(例如绝对定位的元素覆盖了父元素的一部分)。我的则不然。

用法:

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

注意:

明智的做法是将父元素设置为相对位置。如果你有一个绝对定位的伪元素,这个函数只在它基于父元素的维度定位时才会工作(所以父元素必须是相对的…也许粘性或固定也可以....我不知道)。

代码:

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的作品……像往常一样。

这是由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.fromfor...of,但是在代码中使用它们是不必要的。

我解决了这个情况,添加pointer-events: none;在:后css

我将发布解决方案:

  1. offset()方法来获取根元素左上角信息
  2. window.getComputedStyle()方法来获取伪元素风格

请确保根元素position属性被指定为relative,并且absolute被指定为伪元素 小提琴演示是在这里.

.

. < p > 额外的评论:
这个问题是11年前提出的,现在已经有了一个公认的答案。
尽管如此,我还是发布了这个解决方案,原因如下
  • 在伪元素上检测点击事件时,我也有同样的想法。
  • 接受的答案(“添加子元素”)不能满足我的需要。
  • 多亏了这个帖子,我有了解决方案的想法。
  • 如果有人有类似的问题可以通过这篇文章来解决,我会很高兴。

/**
* Click event listener.
*/
$(element).click((e) => {
// mouse cursor position
const mousePoint = { x: e.pageX, y: e.pageY };


if (isMouseOnPseudoElement(mousePoint, e.target, 'before')) {
console.log('[:before] pseudo-element was clicked');
} else if (isMouseOnPseudoElement(mousePoint, e.target, 'after')) {
console.log('[:after] pseudo-element was clicked');
}
});


/**
* Returns the info of whether the mouse cursor is over the pseudo-element.
*
* @param {JSON} point: coordinate of mouse cursor
* @param {DOMElement} element: root element
* @param {String} pseudoType: "before" or "after"
* @returns {JSON}: info of whether the mouse cursor is over the pseudo-element
*/
function isMouseOnPseudoElement(point, element, pseudoType = 'before') {
const pseudoRect = getPseudoElementRect(element, pseudoType);


return point.y >= pseudoRect.top
&& point.y <= pseudoRect.bottom
&& point.x >= pseudoRect.left
&& point.x <= pseudoRect.right;
}


/**
* Gets the rectangle info of the pseudo-element.
*
* @param {DOMElement} element: root element
* @param {String} pseudoType: "before" or "after"
* @returns {JSON}: rectangle info of the pseudo-element
*/
function getPseudoElementRect(element, pseudoType = 'before') {
// top-left info of the root element
const rootOffset = $(element).offset();


// style of the pseudo-element
const pseudoStyle = window.getComputedStyle(element, ':' + pseudoType);


const top = rootOffset.top + parseFloat(pseudoStyle.top);
const left = rootOffset.left + parseFloat(pseudoStyle.left);


const height = parseFloat(pseudoStyle.height);
const width = parseFloat(pseudoStyle.width);
const borderTop = parseFloat(pseudoStyle['border-top-width']);
const borderBottom = parseFloat(pseudoStyle['border-bottom-width']);
const borderLeft = parseFloat(pseudoStyle['border-left-width']);
const borderRight = parseFloat(pseudoStyle['border-right-width']);


// rounds the decimal numbers detected when zooming
return {
top: Math.round(top),
left: Math.round(left),
bottom: Math.round(top + height + borderTop + borderBottom),
right: Math.round(left + width + borderLeft + borderRight)
};
}