Виявляйте лише події клацання на псевдоелементі


213

Мій код:

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

Можливо, хороший шлях ТУТ .
Реза Мамун

Відповіді:


218

Це неможливо; псевдоелементи взагалі не є частиною DOM, тому ви не можете прив’язувати жодні події безпосередньо до них, ви можете прив'язувати лише їх батьківські елементи.

Якщо ви повинні мати обробник клацання на червоній області тільки, ви повинні зробити дочірній елемент, на кшталт span, місце це відразу після відкриття <p>тега, застосувати стилі до p spanзамість p:before, і зв'язуватися з нею.


108
Здається, що в сучасних браузерах це можливо з різними pointer-eventsзначеннями для самого елемента та його псевдоелемента: jsfiddle.net/ZWw3Z/70
Ілля

5
@Ilya Streltsyn дивовижна - не "правильна" відповідь (не працювала б, якщо вам також потрібно було натиснути елемент), але працює чудово для "кнопки закриття" на
divs

pointer-eventsЗгідно з jsfiddel, який опублікував Ілля, не здається зробити трюк для IE9.
користувач393274

@ 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, якщо це має значення (не повинно).
Flater

2
для firefox: jsfiddle.net/wC2p7/16 . Якщо ви працюєте з вкладеними елементами, можливо, вам доведеться обчислити вкладені зрушення відповідно (наприклад, jQuery: $ (e.target) .offset (). Зліва)
bebbi

1
Дивовижна людина спасибі, це працює. Але в браузерах Firefox та інших стандартах, які відповідають стандартам, для перевірки того, чи :afterпотрібно перевіряти курсор миші, if (e.clientX > span.offsetLeft + span.offsetHeight)оскільки ці браузери не мають e.offsetXвластивості. Fiddle
Noitidart

5
Ще крутіше було б, якби jQuery вирішив підтримати, $('a:after').on('click', fn)але я насправді не бачу, що це відбувається: p
Linus Unnebäck

25
Це насправді не буде працювати, якщо ::beforeабо ::afterрозміщений всередині елемента.
лаконбас

74

У сучасних браузерах ви можете спробувати властивість css pointer-events (але це призводить до неможливості виявити події миші на батьківському вузлі):

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: раніше".

Якщо вам все-таки потрібно виявити події миші на головному 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: раніше, тодішній .box не може виявити жодного клацання?
найповажніший сер

Це частково вірно: якщо ваш CSS містить щось на кшталт .box {pointer-events: none;} .box: before {pointer-events: auto;}, тоді єдиним елементом, який підтримує події, є p: before. Все одно пам’ятайте, що js поверне вам головний .box елемент з .box: раніше насправді не живе у DOM.
Фасое

9

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

Завдяки цій статті я зрозумів (за допомогою jQuery), що можу використовувати e.pageYі e.pageXзамість того, щоб перейматися e.offsetY/Xта e.clientY/Xвидавати між браузерами.

Завдяки моїй пробі та помилкам я почав використовувати координати миші clientX та clientY в об’єкті події jQuery. Ці координати дали мені зміщення X і Y миші відносно лівого верхнього кута порту огляду браузера. Однак, читаючи довідник керівництва jQuery 1.4 від Карла Сведберга та Джонатана Чаффера, я побачив, що вони часто посилаються на координати сторінкиX та pageY. Перевіривши оновлену документацію jQuery, я побачив, що це координати, стандартизовані jQuery; і я побачив, що вони дають мені зсув X і Y миші відносно всього документа (не тільки порту перегляду).

Мені сподобалась ця event.pageYідея, тому що вона завжди була б однаковою, як це було відносно документа . Я можу порівняти його з моїм: після батьківського елемента, що використовує 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');
  }
});

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.

Потім він порівнює де миша. Поки миша знаходиться в новоствореному діапазоні змінних, вона повертає істину.

Примітка:

Розумно розмістити батьківський елемент ВІДНОСНО. Якщо у вас абсолютно розташований елемент 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

  };      

}

Підтримка:

Я не знаю .... це схоже, тобто є німим і любить повертати авто як обчислене значення іноді. ВИНАЄТЬСЯ, ЩО РОБОТИ ДОБРО ПРАЦІВАТИ ВСІХ БРОЗЕРІВ, ЯКЩО РОЗМІРИ ВСТАНОВЛЕНІ В CSS. Отже ... встановіть свою висоту та ширину на елементах psuedo і перемістіть їх лише вгорі та вліво. Я рекомендую використовувати його для речей, з якими ви добре не працюєте. Як анімація чи щось таке. Chrome працює ... як завжди.


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

Це відредагована відповідь Фасоу із останніми 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

Жоден із цих відповідей не є достовірним, і мій звичайний не буде набагато надійнішим.

Застереження, якщо ви потрапляєте в щасливий сценарій, коли елемент, на який ви намагаєтесь натиснути, не має прокладки (таким, що весь "внутрішній" простір елемента повністю покритий піделементами), тоді ви може перевірити ціль події клацання проти самого контейнера. Якщо вона збігається, це означає, що ви натиснули елемент: до або після.

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


-2

Ні, але ви можете зробити так

У файл html додайте цей розділ

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

У css ви можете зробити так

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

} Сподіваюся, це допоможе вам

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.