简体   繁体   English

只检测伪元素的点击事件

[英]Only detect click event on pseudo-element

Please see this fiddle: http://jsfiddle.net/ZWw3Z/5/请看这个小提琴: http://jsfiddle.net/ZWw3Z/5/

My code is:我的代码是:

 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.也就是说,我不想在蓝色位上触发点击事件。

This is not possible;这不可能; pseudo-elements are not part of the DOM at all so you can't bind any events directly to them, you can only bind to their parent elements.伪元素根本不是 DOM 的一部分,因此您不能将任何事件直接绑定到它们,只能绑定到它们的父元素。

If you must have a click handler on the red region only, you have to make a child element, like a span , place it right after the opening <p> tag, apply styles to p span instead of p:before , and bind to it.如果你必须只在红色区域有一个点击处理程序,你必须创建一个子元素,比如span ,将它放在开始<p>标签之后,将样式应用到p span而不是p:before ,并绑定到它。

Actually, it is possible.事实上,这是可能的。 You can check if the clicked position was outside of the element, since this will only happen if ::before or ::after was clicked.您可以检查单击的位置是否在元素之外,因为这只会在::before::after被单击时发生。

This example only checks the element to the right but that should work in your case.此示例仅检查右侧的元素,但这应该适用于您的情况。

 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 JSFiddle

On modern browsers you can try with the pointer-events css property (but it leads to the impossibility to detect mouse events on the parent node):现代浏览器上,您可以尝试使用 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;
}

When the event target is your "p" element, you know it is your "p:before".当事件目标是您的“p”元素时,您就知道它是您的“p:before”。

If you still need to detect mouse events on the main p, you may consider the possibility to modify your HTML structure.如果您仍然需要检测主 p 上的鼠标事件,您可以考虑修改您的 HTML 结构的可能性。 You can add a span tag and the following style:您可以添加span标签和以下样式:

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

The event targets are now both the "span" and the "p:before" elements.事件目标现在既是“span”又是“p:before”元素。

Example without jquery: http://jsfiddle.net/2nsptvcu/没有 jquery 的例子: http : //jsfiddle.net/2nsptvcu/

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

Here is the list of browsers supporting pointer-events: http://caniuse.com/#feat=pointer-events以下是支持指针事件的浏览器列表: http : //caniuse.com/#feat=pointer-events

Short Answer:简答:

I did it.我做到了。 I wrote a function for dynamic usage for all the little people out there...我为所有小人物编写了一个动态使用函数......

Working example which displays on the page显示在页面上的工作示例

Working example logging to the console登录到控制台的工作示例

Long Answer:长答案:

...Still did it. ……还是做了。

It took me awhile to do it, since a psuedo element is not really on the page.我花了一段时间才这样做,因为页面上并没有真正的伪元素。 While some of the answers above work in SOME scenarios, they ALL fail to be both dynamic and work in a scenario in which an element is both unexpected in size and position(such as absolute positioned elements overlaying a portion of the parent element).虽然上面的一些答案在某些情况下有效,但它们都不能既是动态的,也不能在元素的大小和位置都出乎意料的情况下工作(例如绝对定位的元素覆盖父元素的一部分)。 Mine does not.我的没有。

Usage:用法:

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

});

How it works:这个怎么运作:

It grabs the height, width, top, and left positions(based on the position away from the edge of the window) of the parent element and grabs the height, width, top, and left positions(based on the edge of the parent container) and compares those values to determine where the psuedo element is on the screen.它抓取父元素的高度、宽度、顶部和左侧位置(基于远离窗口边缘的位置)并抓取高度、宽度、顶部和左侧位置(基于父容器的边缘) ) 并比较这些值以确定伪元素在屏幕上的位置。

It then compares where the mouse is.然后比较鼠标的位置。 As long as the mouse is in the newly created variable range then it returns true.只要鼠标在新创建的变量范围内,它就会返回 true。

Note:笔记:

It is wise to make the parent element RELATIVE positioned.使父元素 RELATIVE 定位是明智的。 If you have an absolute positioned psuedo element, this function will only work if it is positioned based on the parent's dimensions(so the parent has to be relative...maybe sticky or fixed would work too....I dont know).如果你有一个绝对定位的伪元素,这个函数只有在基于父元素的尺寸定位时才有效(所以父元素必须是相对的......也许粘性或固定也可以工作......我不知道)。

Code:代码:

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

  };      

}

Support:支持:

I dont know....it looks like ie is dumb and likes to return auto as a computed value sometimes.我不知道....它看起来像 ie 是愚蠢的,有时喜欢将 auto 作为计算值返回。 IT SEEMS TO WORK WELL IN ALL BROWSERS IF DIMENSIONS ARE SET IN CSS.如果尺寸在 CSS 中设置,它似乎在所有浏览器中都能正常工作。 So...set your height and width on your psuedo elements and only move them with top and left.所以......在你的伪元素上设置你的高度和宽度,并且只在顶部和左侧移动它们。 I recommend using it on things that you are okay with it not working on.我建议将它用于您认为它不起作用的事情。 Like an animation or something.比如动画什么的。 Chrome works...as usual. Chrome 工作......像往常一样。

My answer will work for anyone wanting to click a definitive area of the page.我的答案适用于想要单击页面确定区域的任何人。 This worked for me on my absolutely-positioned :after这对我的绝对定位对我有用 :after

Thanks to this article , I realized (with jQuery) I can use e.pageY and e.pageX instead of worrying about e.offsetY/X and e.clientY/X issue between browsers.感谢这篇文章,我意识到(使用 jQuery)我可以使用e.pageYe.pageX而不是担心浏览器之间的e.offsetY/Xe.clientY/X问题。

Through my trial and error, I started to use the clientX and clientY mouse coordinates in the jQuery event object.通过反复试验,我开始在 jQuery 事件对象中使用 clientX 和 clientY 鼠标坐标。 These coordinates gave me the X and Y offset of the mouse relative to the top-left corner of the browser's view port.这些坐标为我提供了鼠标相对于浏览器视口左上角的 X 和 Y 偏移量。 As I was reading the jQuery 1.4 Reference Guide by Karl Swedberg and Jonathan Chaffer, however, I saw that they often referred to the pageX and pageY coordinates.然而,当我阅读 Karl Swedberg 和 Jonathan Chaffer 的 jQuery 1.4 参考指南时,我看到他们经常提到 pageX 和 pageY 坐标。 After checking the updated jQuery documentation, I saw that these were the coordinates standardized by jQuery;查看更新的jQuery文档后,我看到这些是jQuery标准化的坐标; and, I saw that they gave me the X and Y offset of the mouse relative to the entire document (not just the view port).而且,我看到他们给了我鼠标相对于整个文档(不仅仅是视口)的 X 和 Y 偏移量。

I liked this event.pageY idea because it would always be the same, as it was relative to the document .我喜欢这个event.pageY想法,因为它总是相同的,因为它是相对于文档的 I can compare it to my :after's parent element using offset(), which returns its X and Y also relative to the document.我可以使用 offset() 将它与我的 :after 的父元素进行比较,它返回它的 X 和 Y 也相对于文档。

Therefore, I can come up with a range of "clickable" region on the entire page that never changes.因此,我可以在整个页面上提出一系列永不改变的“可点击”区域。


Here's my demo on codepen .这是我在 codepen 上演示


or if too lazy for codepen, here's the JS:或者如果对 codepen 太懒了,这里是 JS:

* I only cared about the Y values for my example. * 我只关心我的例子的 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');
  }
});

This works for me:这对我有用:

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

Add condition in Click event to restrict the clickable area .在 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演示

This is edited answer by Fasoeu with latest CSS3 and JS ES6这是Fasoeu用最新的 CSS3 和 JS ES6 编辑的答案

Edited demo without using JQuery.不使用 JQuery编辑演示

Shortest example of code:最短的代码示例:

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

Explanation:解释:

pointer-events control detection of events, removing receiving events from target, but keep receiving from pseudo-elements make possible to click on ::before and ::after and you will always know what you are clicking on pseudo-element, however if you still need to click, you put all content in nested element ( span in example), but because we don't want to apply any additional styles, display: contents; pointer-events控制pointer-events检测,从目标中删除接收事件,但继续从伪元素接收使得点击::before::after成为可能,你将始终知道你在点击伪元素,但是如果你仍然需要单击,您将所有内容放在嵌套元素中(例如span ),但是因为我们不想应用任何其他样式,所以display: contents; become very handy solution and it supported by most browsers .成为非常方便的解决方案,大多数浏览器都支持它。 pointer-events: none; as already mentioned in original post also widely supported .正如原帖中已经提到的,也得到了广泛支持

The JavaScript part also used widely supported Array.from and for...of , however they are not necessary to use in code. JavaScript 部分还使用了广泛支持的Array.fromfor...of ,但是它们不是必须在代码中使用的。

None of these answers are reliable, and mine wont be much more reliable.这些答案都不可靠,我的也不可靠。

Caveats aside, if you do get into the lucky scenario where the element you're trying to have clicked doesn't have padding (such that all of the "inner" space of the element is completely covered by sub-elements), then you can check the target of the click event against the container itself.除了警告之外,如果您确实遇到了您尝试单击的元素没有填充的幸运场景(这样元素的所有“内部”空间都被子元素完全覆盖),那么您可以根据容器本身检查单击事件的目标。 If it matches, that means you've clicked a :before or :after element.如果匹配,则意味着您单击了 :before 或 :after 元素。

Obviously this would not be feasible with both types (before and after) however I have implemented it as a hack/speed fix and it is working very well, without a bunch of position checking, which may be inaccurate depending on about a million different factors.显然这对于​​两种类型(之前和之后)都是不可行的,但是我已经将它实现为一个黑客/速度修复并且它运行得很好,没有一堆位置检查,这可能不准确,这取决于大约一百万个不同的因素.

I solved this case with add pointer-events: none;我用 add pointer-events: none;解决了这个问题pointer-events: none; at :after css在 :css 之后

I'll post the solution with:我将发布解决方案:

  1. offset() method to get the top-left corner info of the root element . offset()方法获取根元素左上角信息
  2. window.getComputedStyle() method to get the styles of the pseudo-element . window.getComputedStyle()方法获取伪元素styles

Please make sure the position property of the root element is specified as relative , and absolute for the pseudo-element .请确保根元素position属性指定为relative伪元素指定为absolute

The Fiddle demo is here .小提琴演示在这里
Hope this will help.希望这会有所帮助。

Additional comment:附加评论:
This question was posted 11 years ago, and already has an accepted answer.这个问题是 11 年前发布的,并且已经有一个公认的答案。
The reason why I posted this solution despite the above fact is as follows.尽管有上述事实,但我发布此解决方案的原因如下。

  • I had struggled with the same desire to detect click event on pseudo-element.我一直在努力检测伪元素上的点击事件。
  • The accepted answer ("adding a child element") does not satisfy my needs.接受的答案(“添加子元素”)不能满足我的需求。
  • I've got the idea of the solution thanks to some posts on this thread.多亏了这个线程上的一些帖子,我有了解决方案的想法。
  • I would be happy if someone with a similar problem could solve it with this post.如果有类似问题的人可以通过这篇文章解决它,我会很高兴。

/**
 * 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)
    };
}

Without JQuery i used this for sidebar menu click detectionon pseudo plus icons:在没有 JQuery 的情况下,我将其用于伪加号图标上的边栏菜单单击检测:

HTML: HTML:

<ul>
    <li>MENU ELEMENT</li>
    <li>MENU ELEMENT</li>
    <li>MENU ELEMENT</li>
</ul>

CSS: CSS:

ul { margin: 30px; }
li { display: flex; width: 300px; justify-content: space-between;}
li:after { content: ' +'}

li.c1 { background: red; }
li.c2:after { background: yellow; }

JS:记者:

document.querySelectorAll("li").forEach(function (e) {
        e.addEventListener('click', function(u) {
            let linkWidth = this.offsetWidth;           
            let pseudoWidth = parseFloat(window.getComputedStyle(this, ':after').width);
            const mouseX = u.offsetX;
            if (mouseX > (linkWidth - pseudoWidth)) {
                console.log ("click pseudo");
                this.className = 'c2';
            } else {
                console.log ("click element");
                this.className = 'c1';
            }
        })
});

No,but you can do like this不,但你可以这样做

In html file add this section在 html 文件中添加此部分

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

In css you can do like this在 css 中你可以这样做

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

Hope it will help you希望它会帮助你

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM