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

214

Мой код:

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/

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

Randomblue
источник
1
Как насчет проверки нажал pos? stackoverflow.com/questions/3234977/…
Пожалуйста, прочитайте мое ранее опубликованное решение здесь .
Амр
Возможно, хороший обходной путь ЗДЕСЬ .
Реза Мамун

Ответы:

218

Это невозможно; псевдоэлементы вообще не являются частью DOM, поэтому вы не можете привязать события непосредственно к ним, вы можете привязать только их родительские элементы.

Если у вас должен быть обработчик щелчков только в красной области, вы должны создать дочерний элемент, такой как a span, поместить его сразу после открывающего <p>тега, применить p spanвместо него стили p:beforeи привязать их.

BoltClock
источник
108
Похоже, что в современных браузерах это возможно при разных pointer-eventsзначениях самого элемента и его псевдоэлемента
Илья Стрельцын,
5
@ Удивительный Илья Стрельцын - не «правильный» ответ (не сработает, если вам также нужно нажать на элемент), но блестяще работает для «кнопки закрытия» на
divs
pointer-eventsСудя по всему, не подходит для IE9, согласно сообщению jsfiddel, которое опубликовал Илья.
user393274
@ user393274: IE9 не поддерживает события указателя вне SVG.
BoltClock
1
@ theyuv нет, на этой странице вся строка кликабельна, как слева, так и справа от ссылки. Только сама ссылка имеет другой обработчик кликов, поэтому нажатие на нее не вызывает складывание / развертывание поддерева.
Илья Стрельцын
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, если это имеет значение (не должно).
Флэтер
2
для firefox: jsfiddle.net/wC2p7/16 . При работе с вложенными элементами вам может понадобиться рассчитать вложенные смещения соответствующим образом (например, jQuery: $ (e.target) .offset ().
Left
1
Офигенный мужчина спасибо, все работает. Но в браузерах, совместимых с Firefox и другими стандартами, для проверки, :afterнужно ли навести указатель мыши на них, так if (e.clientX > span.offsetLeft + span.offsetHeight)как эти браузеры не имеют этого e.offsetXсвойства. Скрипка: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
5
Еще круче было бы, если бы jQuery решил поддержать, $('a:after').on('click', fn)но я не вижу, чтобы это произошло: p
Линус Уннебекк
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».

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

Fasoeu
источник
Я не знаю, почему родительский узел. Вы говорите, что если я щелкаю событие что-то вроде .box: before, то .box не может обнаружить щелчок?
самый почтенный сэр
Это частично верно: если ваш CSS содержит что-то вроде .box {pointer-events: none;} .box: before {pointer-events: auto;}, тогда единственный элемент, который все еще поддерживает события, это p: before. В любом случае, помните, что js вернет вам основной элемент .box, поскольку .box: before действительно не живет в DOM.
Фасоу
9

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

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


источник
eventпередается через функцию обработчика событий каждый раз, когда происходит событие. Как щелкать или печатать. Когда мы используем .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
       }
     });

DEMO

Абденнур ТУМИ
источник
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, тем не менее, не является необходимой для использования в коде.

XCanG
источник
0

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

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

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

dudewad
источник
-2

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

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

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

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

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

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

Гопал Богати
источник