Обнаружить только событие клика по псевдоэлементу

Мой код:

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/

Я хотел бы вызвать событие щелчка только на псевдоэлементе (красный бит). То есть я не хочу, чтобы событие щелчка вызывалось синим битом.

14 ответов

Решение

Это невозможно; псевдоэлементы вообще не являются частью 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

В современных браузерах вы можете попробовать использовать свойство 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".

Если вам по-прежнему необходимо обнаруживать события мыши на главной странице, вы можете рассмотреть возможность изменения структуры 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/

Короткий ответ:

Я это сделал. Я даже сделал это в чистом javascript и написал функцию для динамического использования для всех маленьких людей там...

Рабочий пример с функцией, которую вы можете украсть

Длинный ответ:

... все еще сделал это.

Это заняло у меня некоторое время, так как элемент 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 и перемещайте их только сверху и слева. Я рекомендую использовать это на вещах, с которыми у вас все в порядке, если вы не работаете. Как анимация или что-то. Хром работает... как обычно.

Мой ответ будет работать для всех, кто хочет щелкнуть определенную область страницы. Это сработало для меня на моем абсолютно позиционированном: после

Благодаря этой статье я понял (с jQuery) я могу использовать e.pageY а также e.pageX вместо того, чтобы беспокоиться о e.offsetY/X а также e.clientY/X проблема между браузерами.

Методом проб и ошибок я начал использовать координаты мыши clientX и clientY в объекте события jQuery. Эти координаты дали мне смещение по оси X и Y мыши относительно верхнего левого угла порта просмотра браузера. Когда я читал Справочное руководство jQuery 1.4 Карла Сведберга и Джонатана Чеффера, я увидел, что они часто ссылаются на координаты pageX и pageY. После проверки обновленной документации jQuery я увидел, что это были координаты, стандартизированные jQuery; и я увидел, что они дали мне смещение X и Y мыши относительно всего документа (не только порта просмотра).

Мне понравилось это event.pageY идея, потому что она всегда будет такой же, как и по отношению к документу. Я могу сравнить его с родительским элементом my:after, используя offset(), который возвращает свои 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');
  }
});

Это работает для меня:

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

Это отредактированный ответ 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, однако их необязательно использовать в коде.

Я решил этот случай добавлением pointer-events: none; в: после css

Добавьте условие в событие 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

        $('span').after(function() {
        $(this).click(function() {
            
        });
    });

Без JQuery я использовал это для обнаружения кликов меню боковой панели на псевдоплюсовых значках:

HTML:

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

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

Ни один из этих ответов не является надежным, и мой не будет намного более надежным.

Оставьте в стороне, если вы попадаете в счастливый случай, когда элемент, на который вы пытаетесь щелкнуть, не имеет отступов (так что все "внутреннее" пространство элемента полностью покрыто подэлементами), тогда вы может проверить цель события click относительно самого контейнера. Если он совпадает, это означает, что вы щелкнули элемент: before или: after.

Очевидно, что это не было бы осуществимо с обоими типами (до и после), однако я реализовал это как исправление взлома / скорости, и оно работает очень хорошо, без кучки проверки позиции, которая может быть неточной в зависимости от миллиона различных факторов,

Я опубликую решение с:

  1. offset()метод для получения информации о верхнем левом углу корневого элемента .
  2. window.getComputedStyle()метод для получения стилей псевдоэлемента .

Пожалуйста, убедитесь, что positionсвойство корневого элемента указывается как relative, и absoluteдля псевдоэлемента .

Демо Fiddle здесь.
Надеюсь, это поможет.

Дополнительный комментарий:
этот вопрос был опубликован 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)
    };
}

Нет, но вы можете сделать это

В HTML-файл добавить этот раздел

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

В CSS вы можете сделать это

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

} Надеюсь, это поможет вам

Другие вопросы по тегам