Wykrywa tylko zdarzenie kliknięcia na pseudoelemencie

213

Mój kod to:

p {
    position: relative;
    background-color: blue;
}

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

Zobacz to skrzypce: http://jsfiddle.net/ZWw3Z/5/

Chciałbym wywołać zdarzenie kliknięcia tylko na pseudoelemencie (czerwony bit). Oznacza to, że nie chcę, aby zdarzenie kliknięcia było uruchamiane na niebieskim bicie.

Randomblue
źródło
1
Co powiesz na sprawdzenie klikniętej pozycji? stackoverflow.com/questions/3234977/…
Proszę przeczytać moje wcześniej opublikowane rozwiązanie tutaj .
Amr
Prawdopodobnie dobre obejście TUTAJ .
Reza Mamun

Odpowiedzi:

218

To jest niemożliwe; pseudoelementy w ogóle nie są częścią DOM, więc nie możesz powiązać żadnych zdarzeń bezpośrednio z nimi, możesz powiązać tylko z ich elementami nadrzędnymi.

Jeśli musisz mieć moduł obsługi kliknięć tylko na czerwonym obszarze, musisz utworzyć element podrzędny, taki jak a span, umieścić go bezpośrednio za <p>tagiem otwierającym , zastosować style p spanzamiast p:beforei powiązać z nim.

BoltClock
źródło
108
Wydaje się, że w nowoczesnych przeglądarkach jest to możliwe z różnymi pointer-eventswartościami dla samego elementu i jego pseudoelementu: jsfiddle.net/ZWw3Z/70
Ilya Streltsyn
5
@Ilya Streltsyn niesamowita - nie jest to „poprawna” odpowiedź (nie działałaby, gdybyś także musiał kliknąć element), ale działa znakomicie na „przycisk zamknięcia” na
divach
pointer-eventsnie wydaje się robić sztuczki dla IE9, zgodnie z jsfiddel, który opublikował Ilya.
user393274,
@ user393274: IE9 nie obsługuje zdarzeń wskaźnika poza SVG.
BoltClock
1
@ nie, na tej stronie można kliknąć całą linię, zarówno po lewej, jak i po prawej od linku. Tylko sam link ma inną procedurę obsługi kliknięć, więc kliknięcie go nie powoduje poddania składanego / rozwijanego poddrzewa.
Ilya Streltsyn
131

W rzeczywistości jest to możliwe. Możesz sprawdzić, czy pozycja kliknięcia znajdowała się poza elementem, ponieważ nastąpi to tylko po kliknięciu ::beforelub ::afterkliknięciu.

Ten przykład sprawdza tylko element po prawej, ale to powinno działać w twoim przypadku.

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

Linus Unnebäck
źródło
1
Dla mnie to nie działa, tekst jest podświetlany po kliknięciu jednego z nich. Używanie Firefoksa, jeśli ma to znaczenie (nie powinno).
Flater
2
dla firefox: jsfiddle.net/wC2p7/16 . Podczas pracy z elementami zagnieżdżonymi może być konieczne obliczenie odpowiednio zagnieżdżonych przesunięć (np. JQuery: $ (e.target) .offset ().
Left
1
Świetny człowiek dzięki, to działa. Ale w przeglądarkach Firefox i innych zgodnych ze standardami, aby sprawdzić, czy najedziesz myszką :after, musisz sprawdzić, if (e.clientX > span.offsetLeft + span.offsetHeight)ponieważ te przeglądarki nie mają tej e.offsetXwłaściwości. Fiddle: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
5
Byłoby jeszcze fajniej, gdyby jQuery zdecydowało się na wsparcie, $('a:after').on('click', fn)ale tak naprawdę nie widzę, żeby tak się działo: p
Linus Unnebäck
25
To faktycznie nie zadziała, jeśli ::beforelub ::afterjest umieszczony wewnątrz elementu.
laconbass
74

W nowoczesnych przeglądarkach możesz wypróbować właściwość css wskaźnika-events (ale prowadzi to do niemożności wykrycia zdarzeń myszy w węźle nadrzędnym):

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

Kiedy celem zdarzenia jest Twój element „p”, wiesz, że jest to Twój element „p: przed”.

Jeśli nadal musisz wykrywać zdarzenia myszy na głównym p, możesz rozważyć możliwość modyfikacji struktury HTML. Można dodać rozpiętości znacznik i następujący styl:

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

Celami zdarzenia są teraz zarówno elementy „span”, jak i „p: before”.

Przykład bez jquery: http://jsfiddle.net/2nsptvcu/

Przykład z jquery: http://jsfiddle.net/0vygmnnb/

Oto lista przeglądarek obsługujących zdarzenia wskaźnika: http://caniuse.com/#feat=pointer-events

Fasoeu
źródło
Nie wiem, dlaczego jest węzeł nadrzędny. Czy mówisz, że jeśli zdarzenie kliknięcia na czymś takim jak .box: wcześniej, wtedy .box nie może wykryć żadnego kliknięcia?
najbardziej czcigodny panie
To częściowo prawda: jeśli Twój CSS zawiera coś w rodzaju .box {pointer-events: none;} .box: before {pointer-events: auto;}, wówczas jedynym elementem, który nadal obsługuje zdarzenia, jest p: przed. Pamiętaj jednak, że js zwróci ci główny element .box, ponieważ .box: wcześniej tak naprawdę nie istnieje w DOM.
Fasoeu,
9

Moja odpowiedź będzie działać dla każdego, kto chce kliknąć określony obszar strony. To działało dla mnie w mojej absolutnie pozycji: po

Dzięki tym artykule , zdałem sobie sprawę (z jQuery) mogę użyć e.pageYi e.pageXzamiast martwić e.offsetY/Xi e.clientY/Xproblem między przeglądarkami.

Dzięki próbom i błędom zacząłem używać współrzędnych myszy clientX i clientY w obiekcie zdarzenia jQuery. Te współrzędne dały mi przesunięcie X i Y myszy w stosunku do lewego górnego rogu portu widoku przeglądarki. Jednak podczas czytania przewodnika jQuery 1.4 autorstwa Karla Swedberga i Jonathana Chaffera zauważyłem, że często odnoszą się one do współrzędnych pageX i pageY. Po sprawdzeniu zaktualizowanej dokumentacji jQuery zobaczyłem, że były to współrzędne znormalizowane przez jQuery; i widziałem, że dali mi przesunięcie X i Y myszy względem całego dokumentu (nie tylko portu widoku).

Podobał mi się ten event.pageYpomysł, ponieważ zawsze będzie taki sam, ponieważ był związany z dokumentem . Mogę porównać go do elementu nadrzędnego my: after za pomocą offset (), który zwraca X i Y również względem dokumentu.

Dlatego mogę wymyślić zakres „klikalnego” regionu na całej stronie, który nigdy się nie zmienia.


Oto moje demo na codepen .


lub jeśli jest zbyt leniwy, aby zastosować codepen, oto JS:

* W moim przykładzie dbałem tylko o wartości 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
źródło
6

To działa dla mnie:

$('#element').click(function (e) {
        if (e.offsetX > e.target.offsetLeft) {
            // click on element
        }
         else{
           // click on ::before element
       }
});
Stóg
źródło
6

Krótka odpowiedź:

Ja to zrobiłem. Napisałem funkcję do dynamicznego użycia dla wszystkich małych ludzi ...

Przykład roboczy, który wyświetla się na stronie

Przykład działającego logowania do konsoli

Długa odpowiedź:

... Nadal to zrobiłem.

Zajęło mi to trochę czasu, ponieważ element psuedo nie jest tak naprawdę na stronie. Chociaż niektóre z powyższych odpowiedzi działają w NIEKTÓRYCH scenariuszach, WSZYSTKIE nie działają zarówno dynamicznie, jak i działają w scenariuszu, w którym element ma zarówno nieoczekiwany rozmiar, jak i pozycję (np. Elementy pozycjonowane bezwzględnie nakładające się na część elementu macierzystego). Mój nie.

Stosowanie:

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

});

Jak to działa:

Pobiera wysokość, szerokość, górną i lewą pozycję (w oparciu o pozycję od krawędzi okna) elementu nadrzędnego i chwyta wysokość, szerokość, górną i lewą pozycję (w oparciu o krawędź pojemnika nadrzędnego ) i porównuje te wartości, aby ustalić, gdzie element psuedo znajduje się na ekranie.

Następnie porównuje, gdzie jest mysz. Tak długo, jak mysz znajduje się w nowo utworzonym zakresie zmiennych, zwraca wartość true.

Uwaga:

Rozsądnie jest ustawić element macierzysty RELATIVE. Jeśli masz bezwzględnie pozycjonowany element psuedo, ta funkcja będzie działać tylko wtedy, gdy zostanie ustawiona w oparciu o wymiary rodzica (więc rodzic musi być względny ... może lepki lub naprawiony też by działał .... nie wiem).

Kod:

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

  };      

}

Wsparcie:

Nie wiem .... wygląda na to, że jest głupi i czasami zwraca wartość auto jako wartość obliczoną. Wydaje się, że działa dobrze we wszystkich przeglądarkach, jeśli wymiary są ustawione w CSS. Więc ... ustaw wysokość i szerokość elementów psuedo i przesuwaj je tylko u góry i po lewej stronie. Zalecam używanie go do rzeczy, na których jesteś w porządku, gdy nie działa. Jak animacja czy coś takiego. Chrome działa ... jak zwykle.


źródło
eventjest przekazywany przez funkcję obsługi zdarzenia za każdym razem, gdy zdarzenie jest uruchamiane. Jak klikanie lub pisanie. Kiedy korzystamy z tej .click()funkcji, czekamy na zdarzenie click. Możemy sprecyzować i powiedzieć, .click(function(e){...})aby wydarzenie było, eale dopuszczalne jest również użycie event.
3
psEUdo, nie psUEdo!
biziclop
Powyższy kod nie działa, ponieważ eventjest niezdefiniowany.
Sebastian Zartner
@SebastianZartner - Wydarzenie jest słowem kluczowym. To jest, gdy użytkownik coś robi. Dosłownie wyjaśniłem te dwa komentarze powyżej komentarza. Gdy użytkownik wchodzi w interakcję ze stroną, zdarzenie jest odpalane (w większości przypadków). Zdarzenie jest słowem kluczowym przychodzącym z detektora zdarzeń „.click ()”. Jeśli programista chce użyć innej nazwy dla zdarzenia, może skonfigurować ją w detektorze zdarzeń, np. „.Click (e)”, co jest bardziej powszechnym sposobem na jej zobaczenie. JEDNAK ... mimo że mam link działający powyżej, dołączę również taki, który jest bardziej oczywisty i nie loguje się do konsoli, ale zamiast strony potrzebujących.
Powinienem wspomnieć, że nie działa w Firefoksie, ponieważ eventjest niezdefiniowany. Działa jednak w Chrome i Edge.
Sebastian Zartner
2

Dodaj warunek w zdarzeniu Click, aby ograniczyć obszar klikalny.

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

PRÓBNY

Abdennour TOUMI
źródło
1

To jest edytowana odpowiedź Fasoeu z najnowszym CSS3 i JS ES6

Edytowane demo bez użycia JQuery.

Najkrótszy przykład kodu:

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

Wyjaśnienie:

pointer-eventskontroluj wykrywanie zdarzeń, usuwając odbieranie zdarzeń od celu, ale wciąż odbieraj z pseudoelementów, możesz klikać ::beforei ::afterzawsze będziesz wiedział, co klikasz na pseudoelement, jednak jeśli nadal musisz klikać, umieszczasz całą treść w zagnieżdżonym elemencie ( spanna przykład), ale ponieważ nie chcemy stosować żadnych dodatkowych stylów, display: contents;stajemy się bardzo przydatnym rozwiązaniem i jest obsługiwane przez większość przeglądarek . pointer-events: none;jak już wspomniano w oryginalnym poście, również szeroko wspierane .

Część JavaScript również stosowany szeroko wspierany Array.froma for...ofjednak nie są one konieczne do wykorzystania w kodzie.

XCanG
źródło
0

Żadna z tych odpowiedzi nie jest wiarygodna, a moja nie będzie o wiele bardziej wiarygodna.

Odkładając na bok zastrzeżenia, jeśli przejdziesz do szczęśliwego scenariusza, w którym element, który chcesz kliknąć, nie ma wypełnienia (tak, że cała „wewnętrzna” przestrzeń elementu jest całkowicie zakryta przez podelementy), wtedy może sprawdzić cel zdarzenia kliknięcia względem samego kontenera. Jeśli pasuje, oznacza to, że kliknąłeś element: przed lub po.

Oczywiście nie byłoby to możliwe w przypadku obu typów (przed i po), ale zaimplementowałem go jako poprawkę hack / speed i działa bardzo dobrze, bez wiązki sprawdzania pozycji, która może być niedokładna w zależności od około miliona różnych czynników .

dudewad
źródło
-2

Nie, ale możesz to zrobić

W pliku HTML dodaj tę sekcję

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

W css możesz to zrobić w ten sposób

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

} Mam nadzieję, że ci to pomoże

Gopal Bogati
źródło