Klickereignis nur auf Pseudoelement erkennen

213

Mein Code lautet:

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

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

Bitte sehen Sie diese Geige: http://jsfiddle.net/ZWw3Z/5/

Ich möchte ein Klickereignis nur für das Pseudoelement (das rote Bit) auslösen. Das heißt, ich möchte nicht, dass das Klickereignis auf dem blauen Bit ausgelöst wird.

Zufälliges Blau
quelle
1
Wie wäre es mit der Überprüfung der angeklickten Position? stackoverflow.com/questions/3234977/…
Bitte lesen Sie meine zuvor veröffentlichte Lösung hier .
Amr
Möglicherweise eine gute Problemumgehung HIER .
Reza Mamun

Antworten:

218

Das ist nicht möglich; Pseudoelemente sind überhaupt nicht Teil des DOM, daher können Sie keine Ereignisse direkt an sie binden, sondern nur an ihre übergeordneten Elemente.

Wenn Sie nur einen Klick-Handler für den roten Bereich haben müssen, müssen Sie ein untergeordnetes Element wie a spanerstellen, es direkt nach dem öffnenden <p>Tag platzieren, Stile auf p spananstelle von anwenden p:beforeund daran binden.

BoltClock
quelle
108
Es scheint, dass dies in modernen Browsern mit unterschiedlichen pointer-eventsWerten für das Element selbst und sein Pseudoelement möglich ist
Ilya Streltsyn
5
@Ilya Streltsyn erstaunlich - nicht die 'richtige' Antwort (würde nicht funktionieren, wenn Sie auch auf das Element klicken müssten), aber funktioniert hervorragend für einen 'Schließen-Button' auf Divs
RozzA
pointer-eventsLaut dem von Ilya geposteten jsfiddel scheint der Trick für IE9 nicht zu funktionieren.
user393274
@ user393274: IE9 unterstützt keine Zeigerereignisse außerhalb von SVG.
BoltClock
1
@theyuv nein, auf dieser Seite kann die gesamte Zeile sowohl links als auch rechts vom Link angeklickt werden. Nur der Link selbst verfügt über einen anderen Klick-Handler. Wenn Sie also darauf klicken, wird kein Teilbaum zum Falten / Entfalten ausgelöst.
Ilya Streltsyn
131

Eigentlich ist es möglich. Sie können überprüfen, ob sich die angeklickte Position außerhalb des Elements befand, da dies nur geschieht, wenn ::beforeoder ::aftergeklickt wurde.

In diesem Beispiel wird nur das Element rechts überprüft, dies sollte jedoch in Ihrem Fall funktionieren.

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
quelle
1
Es funktioniert bei mir nicht, der Text wird hervorgehoben, wenn ich auf einen der beiden klicke. Verwenden von Firefox, wenn es darauf ankommt (sollte nicht).
Flater
2
für Firefox: jsfiddle.net/wC2p7/16 . Wenn Sie mit verschachtelten Elementen arbeiten, müssen Sie möglicherweise verschachtelte Offsets entsprechend berechnen (z. B. jQuery: $ (e.target) .offset (). Left)
bebbi
1
Super Mann, danke, es funktioniert. In Firefox- und anderen standardkonformen Browsern müssen Sie zum Testen, ob Sie mit der Maus darüber fahren :after, überprüfen, if (e.clientX > span.offsetLeft + span.offsetHeight)da diese Browser nicht über die e.offsetXEigenschaft verfügen . Geige: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
5
Noch cooler wäre es, wenn sich jQuery für die Unterstützung entscheiden würde, $('a:after').on('click', fn)aber ich sehe das nicht wirklich: p
Linus Unnebäck
25
Dies funktioniert tatsächlich nicht, wenn ::beforeoder ::afterinnerhalb des Elements positioniert ist.
Laconbass
74

In modernen Browsern können Sie es mit der Eigenschaft css für Zeigerereignisse versuchen (dies führt jedoch dazu, dass Mausereignisse auf dem übergeordneten Knoten nicht erkannt werden können):

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

Wenn das Ereignisziel Ihr "p" -Element ist, wissen Sie, dass es Ihr "p: before" ist.

Wenn Sie weiterhin Mausereignisse auf dem Haupt-P erkennen müssen, können Sie die Möglichkeit in Betracht ziehen, Ihre HTML-Struktur zu ändern. Sie können ein Span- Tag und den folgenden Stil hinzufügen :

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

Die Ereignisziele sind jetzt sowohl die Elemente "span" als auch "p: before".

Beispiel ohne jquery: http://jsfiddle.net/2nsptvcu/

Beispiel mit jquery: http://jsfiddle.net/0vygmnnb/

Hier ist die Liste der Browser, die Zeigerereignisse unterstützen: http://caniuse.com/#feat=pointer-events

Fasoeu
quelle
Ich weiß nicht, warum Elternknoten ist. Wollen Sie damit sagen, dass wenn ich das Klickereignis auf so etwas wie .box: vor habe, die dann .box keinen Klick erkennen kann?
ehrwürdigster Herr
Das ist teilweise richtig: Wenn Ihr CSS so etwas wie .box {Zeigerereignisse: keine;} .box: vor {Zeigerereignisse: auto;} enthält, ist das einzige Element, das Ereignisse noch unterstützt, p: vor. Denken Sie trotzdem daran, dass js Ihnen das Hauptelement .box zurückgibt, da .box: before nicht wirklich in The DOM lebt.
Fasoeu
9

Meine Antwort funktioniert für alle, die auf einen bestimmten Bereich der Seite klicken möchten. Das hat bei mir auf meiner absolut positionierten: nachher funktioniert

Dank dieses Artikels wurde mir klar, dass ich (mit jQuery) verwenden kann e.pageYund e.pageXanstatt mir Sorgen um e.offsetY/Xund e.clientY/XProbleme zwischen Browsern zu machen.

Durch meinen Versuch und Irrtum begann ich, die Mauskoordinaten clientX und clientY im jQuery-Ereignisobjekt zu verwenden. Diese Koordinaten gaben mir den X- und Y-Versatz der Maus relativ zur oberen linken Ecke des Ansichtsfensters des Browsers. Als ich das jQuery 1.4-Referenzhandbuch von Karl Swedberg und Jonathan Chaffer las, stellte ich jedoch fest, dass sie sich häufig auf die Koordinaten pageX und pageY bezogen. Nachdem ich die aktualisierte jQuery-Dokumentation überprüft hatte, stellte ich fest, dass dies die von jQuery standardisierten Koordinaten waren. und ich sah, dass sie mir den X- und Y-Versatz der Maus relativ zum gesamten Dokument (nicht nur zum Ansichtsfenster) gaben.

Ich mochte diese event.pageYIdee, weil sie immer dieselbe sein würde, da sie relativ zum Dokument war . Ich kann es mit dem übergeordneten Element von my: after mit offset () vergleichen, das sein X und Y auch relativ zum Dokument zurückgibt.

Daher kann ich auf der gesamten Seite eine Reihe von "anklickbaren" Regionen erstellen, die sich nie ändern.


Hier ist meine Demo zum Codepen .


oder wenn zu faul für Codepen, hier ist die JS:

* Ich habe mich nur um die Y-Werte für mein Beispiel gekümmert.

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
quelle
6

Das funktioniert bei mir:

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

Kurze Antwort:

Ich hab es gemacht. Ich habe eine Funktion für den dynamischen Gebrauch für all die kleinen Leute da draußen geschrieben ...

Arbeitsbeispiel, das auf der Seite angezeigt wird

Arbeitsbeispiel Protokollierung an der Konsole

Lange Antwort:

... hat es immer noch getan.

Ich habe eine Weile dafür gebraucht, da ein Pseudo-Element nicht wirklich auf der Seite ist. Während einige der obigen Antworten in EINIGEN Szenarien funktionieren, sind ALLE nicht dynamisch und funktionieren in einem Szenario, in dem ein Element sowohl in Größe als auch in Position unerwartet ist (z. B. absolut positionierte Elemente, die einen Teil des übergeordneten Elements überlagern). Meins nicht.

Verwendung:

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

});

Wie es funktioniert:

Es erfasst die Positionen Höhe, Breite, oben und links (basierend auf der Position vom Rand des Fensters entfernt) des übergeordneten Elements und erfasst die Positionen Höhe, Breite, oben und links (basierend auf der Kante des übergeordneten Containers) ) und vergleicht diese Werte, um festzustellen, wo sich das Pseudo-Element auf dem Bildschirm befindet.

Anschließend wird verglichen, wo sich die Maus befindet. Solange sich die Maus im neu erstellten Variablenbereich befindet, wird true zurückgegeben.

Hinweis:

Es ist ratsam, das übergeordnete Element RELATIV zu positionieren. Wenn Sie ein absolut positioniertes Pseudo-Element haben, funktioniert diese Funktion nur, wenn es basierend auf den Abmessungen des Elternteils positioniert ist (also muss das Elternteil relativ sein ... vielleicht würde auch klebrig oder fest funktionieren ... ich weiß nicht).

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

  };      

}

Unterstützung:

Ich weiß nicht ... es sieht so aus, als wäre es dumm und gibt manchmal gerne Auto als berechneten Wert zurück. Es scheint in allen Browsern gut zu funktionieren, wenn die Abmessungen in CSS festgelegt sind. Also ... stellen Sie Ihre Höhe und Breite auf Ihre Pseudo-Elemente ein und verschieben Sie sie nur nach oben und links. Ich empfehle es für Dinge zu verwenden, bei denen Sie damit einverstanden sind, dass es nicht funktioniert. Wie eine Animation oder so. Chrome funktioniert ... wie gewohnt.


quelle
eventwird jedes Mal, wenn ein Ereignis ausgelöst wird, durch eine Ereignishandlerfunktion geleitet. Wie klicken oder tippen. Wenn wir die .click()Funktion verwenden, warten wir auf das Klickereignis. Wir könnten spezifizieren und sagen .click(function(e){...}), dass ein Ereignis sein soll, eaber es ist auch akzeptabel, es nur zu verwenden event.
3
psEUdo, nicht psUEdo!
Biziclop
Der obige Code funktioniert nicht, da er eventundefiniert ist.
Sebastian Zartner
@SebastianZartner - Ereignis ist ein Schlüsselwort. Es ist, wenn der Benutzer etwas tut. Ich habe diese beiden Kommentare über Ihrem Kommentar buchstäblich erklärt. Wenn der Benutzer mit der Seite interagiert, wird (in den meisten Fällen) ein Ereignis ausgelöst. Ereignis ist ein Schlüsselwort, das vom Ereignis-Listener ".click ()" eingegeben wird. Wenn ein Entwickler einen anderen Namen für das Ereignis verwenden möchte, kann er ihn im Ereignis-Listener wie ".click (e)" einrichten. Dies ist eine häufigere Methode, um ihn anzuzeigen. JEDOCH ... obwohl ich oben einen Link habe, werde ich auch einen einfügen, der offensichtlicher ist und sich nicht auf der Konsole, sondern auf der Seite für Bedürftige anmeldet.
Ich hätte erwähnen sollen, dass es in Firefox nicht funktioniert, weil es eventundefiniert ist. Es funktioniert jedoch in Chrome und Edge.
Sebastian Zartner
2

Fügen Sie im Click-Ereignis eine Bedingung hinzu, um den anklickbaren Bereich einzuschränken.

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

Abdennour TOUMI
quelle
1

Dies ist eine bearbeitete Antwort von Fasoeu mit den neuesten CSS3 und JS ES6

Demo ohne Verwendung von JQuery bearbeitet .

Kürzestes Beispiel für 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);
};

Erläuterung:

pointer-eventsSteuern Sie die Erkennung von Ereignissen, entfernen Sie empfangende Ereignisse vom Ziel, aber empfangen Sie weiterhin von Pseudoelementen. Klicken Sie auf ::beforeund ::afterSie werden immer wissen, was Sie auf Pseudoelemente klicken. Wenn Sie jedoch noch klicken müssen, fügen Sie den gesamten Inhalt ein in verschachtelten Elementen ( spanim Beispiel), aber da wir keine zusätzlichen Stile anwenden möchten, display: contents;werden Sie zu einer sehr praktischen Lösung, die von den meisten Browsern unterstützt wird . pointer-events: none;wie bereits in Original - Beitrag erwähnt auch allgemein unterstützt .

Der JavaScript-Teil wird ebenfalls weitgehend unterstützt Array.fromund for...ofmuss jedoch nicht im Code verwendet werden.

XCanG
quelle
0

Keine dieser Antworten ist zuverlässig, und meine wird nicht viel zuverlässiger sein.

Abgesehen von den Vorsichtsmaßnahmen, wenn Sie in das glückliche Szenario geraten, in dem das Element, auf das Sie klicken möchten, nicht aufgefüllt ist (so dass der gesamte "innere" Raum des Elements vollständig von Unterelementen bedeckt ist), dann sind Sie es kann das Ziel des Klickereignisses mit dem Container selbst vergleichen. Wenn es übereinstimmt, bedeutet dies, dass Sie auf ein: vor oder: nach Element geklickt haben.

Natürlich wäre dies mit beiden Typen (vorher und nachher) nicht möglich, aber ich habe es als Hack / Speed-Fix implementiert und es funktioniert sehr gut, ohne eine Reihe von Positionsprüfungen, die abhängig von etwa einer Million verschiedener Faktoren ungenau sein können .

Dudewad
quelle
-2

Nein, aber so kannst du es machen

Fügen Sie in der HTML-Datei diesen Abschnitt hinzu

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

In CSS können Sie dies tun

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

} Hoffe es wird dir helfen

Gopal Bogati
quelle