Détecte uniquement l'événement de clic sur le pseudo-élément

214

Mon code est:

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

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

Veuillez voir ce violon: http://jsfiddle.net/ZWw3Z/5/

Je voudrais déclencher un événement de clic uniquement sur le pseudo-élément (le bit rouge). Autrement dit, je ne veux pas que l'événement click soit déclenché sur le bit bleu.

Randomblue
la source
1
Que diriez-vous de vérifier la position cliquée? stackoverflow.com/questions/3234977/…
Veuillez lire ma solution précédemment publiée ici .
Amr
Peut-être une bonne solution de contournement ICI .
Reza Mamun

Réponses:

218

Ce n'est pas possible; les pseudo-éléments ne font pas du tout partie du DOM, vous ne pouvez donc pas leur lier directement des événements, vous ne pouvez les lier qu'à leurs éléments parents.

Si vous ne devez avoir un gestionnaire de clic que sur la région rouge, vous devez créer un élément enfant, comme un span, le placer juste après la <p>balise d' ouverture , appliquer des styles à la p spanplace de p:before, et y lier.

BoltClock
la source
108
Il semble que dans les navigateurs modernes, cela soit possible avec différentes pointer-eventsvaleurs pour l'élément lui-même et son pseudo-élément: jsfiddle.net/ZWw3Z/70
Ilya Streltsyn
5
@Ilya Streltsyn incroyable - pas la réponse «correcte» (ne fonctionnerait pas si vous deviez également cliquer sur l'élément) mais fonctionne brillamment pour un «bouton de fermeture» sur les divs
RozzA
pointer-eventsne semble pas faire l'affaire pour IE9, selon le jsfiddel que Ilya a publié.
user393274
@ user393274: IE9 ne prend pas en charge les événements de pointeur en dehors de SVG.
BoltClock
1
@theyuv no, sur cette page, toute la ligne est cliquable, à gauche et à droite du lien. Seul le lien lui-même a un gestionnaire de clics différent, donc cliquer dessus ne déclenche pas le sous-arbre de pliage / dépliage.
Ilya Streltsyn
131

En fait, c'est possible. Vous pouvez vérifier si la position cliquée était en dehors de l'élément, car cela ne se produira que si ::beforeou::after été cliqué.

Cet exemple vérifie uniquement l'élément à droite, mais cela devrait fonctionner dans votre cas.

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
la source
1
Cela ne fonctionne pas pour moi, le texte est mis en surbrillance lorsque je clique sur l'un ou l'autre. Utiliser Firefox, si c'est important (ne devrait pas).
Flater
2
pour firefox: jsfiddle.net/wC2p7/16 . Si vous travaillez avec des éléments imbriqués, vous devrez peut-être calculer les décalages imbriqués selon les besoins (par exemple, jQuery: $ (e.target) .offset ().
Left
1
Homme génial merci, ça fonctionne. Mais dans Firefox et d'autres navigateurs conformes aux normes, pour tester si :aftervous passez la souris, vous devez vérifier if (e.clientX > span.offsetLeft + span.offsetHeight)que ces navigateurs n'ont pas la e.offsetXpropriété. Violon: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
5
Encore plus cool serait si jQuery décidait de supporter $('a:after').on('click', fn)mais je ne vois pas vraiment ce qui se passe: p
Linus Unnebäck
25
Cela ne fonctionnera pas si ::beforeou ::afterest positionné à l'intérieur de l'élément.
laconbass
74

Sur les navigateurs modernes, vous pouvez essayer avec la propriété css pointer-events (mais cela conduit à l'impossibilité de détecter les événements de souris sur le nœud parent):

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

Lorsque la cible de l'événement est votre élément "p", vous savez que c'est votre "p: avant".

Si vous avez encore besoin de détecter des événements de souris sur le p principal, vous pouvez envisager la possibilité de modifier votre structure HTML. Vous pouvez ajouter une balise span et le style suivant:

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

Les cibles d'événement sont désormais les éléments "span" et "p: before".

Exemple sans jquery: http://jsfiddle.net/2nsptvcu/

Exemple avec jquery: http://jsfiddle.net/0vygmnnb/

Voici la liste des navigateurs prenant en charge les événements de pointeur: http://caniuse.com/#feat=pointer-events

Fasoeu
la source
Je ne sais pas pourquoi le nœud parent est. Voulez-vous dire que si je l'événement de clic sur quelque chose comme .box: avant, puis .box ne peut détecter aucun clic?
plus vénérable monsieur le
C'est partiellement vrai: si votre CSS contient quelque chose comme .box {pointer-events: none;} .box: before {pointer-events: auto;} alors le seul élément qui supporte toujours les événements est p: before. N'oubliez pas que js vous rendra l'élément principal .box car .box: before ne vit pas vraiment dans le DOM.
Fasoeu
9

Ma réponse fonctionnera pour tous ceux qui souhaitent cliquer sur une zone définitive de la page. Cela a fonctionné pour moi sur mon positionnement absolu: après

Grâce à cet article , j'ai réalisé (avec jQuery) que je peux utiliser e.pageYet e.pageXau lieu de m'inquiéter e.offsetY/Xet de e.clientY/Xpasser d'un navigateur à l'autre.

Au cours de mes essais et erreurs, j'ai commencé à utiliser les coordonnées de la souris clientX et clientY dans l'objet d'événement jQuery. Ces coordonnées m'ont donné le décalage X et Y de la souris par rapport au coin supérieur gauche du port d'affichage du navigateur. Cependant, en lisant le Guide de référence jQuery 1.4 de Karl Swedberg et Jonathan Chaffer, j'ai vu qu'ils faisaient souvent référence aux coordonnées pageX et pageY. Après avoir vérifié la documentation jQuery mise à jour, j'ai vu qu'il s'agissait des coordonnées normalisées par jQuery; et, j'ai vu qu'ils m'ont donné le décalage X et Y de la souris par rapport à l'ensemble du document (pas seulement le port d'affichage).

J'ai aimé cette event.pageYidée car elle serait toujours la même, car elle était relative au document . Je peux le comparer à l'élément parent de my: after en utilisant offset (), qui renvoie ses X et Y également par rapport au document.

Par conséquent, je peux proposer une gamme de régions "cliquables" sur la page entière qui ne change jamais.


Voici ma démo sur codepen .


ou si trop paresseux pour codepen, voici le JS:

* Je ne me souciais que des valeurs Y pour mon exemple.

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
la source
6

Cela fonctionne pour moi:

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

Réponse courte:

Je l'ai fait. J'ai écrit une fonction pour une utilisation dynamique pour tous les petits gens là-bas ...

Exemple de travail qui s'affiche sur la page

Exemple de travail de connexion à la console

Longue réponse:

... Je l'ai toujours fait.

Il m'a fallu un certain temps pour le faire, car un élément pseudo n'est pas vraiment sur la page. Bien que certaines des réponses ci-dessus fonctionnent dans CERTAINS scénarios, elles ne parviennent PAS toutes à la fois à être dynamiques et à fonctionner dans un scénario dans lequel un élément est à la fois inattendu en taille et en position (comme des éléments positionnés en absolu superposant une partie de l'élément parent). La mienne non.

Usage:

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

});

Comment ça fonctionne:

Il saisit la hauteur, la largeur, le haut et la gauche (en fonction de la position éloignée du bord de la fenêtre) de l'élément parent et saisit la hauteur, la largeur, le haut et la gauche (en fonction du bord du conteneur parent) ) et compare ces valeurs pour déterminer où se trouve l'élément pseudo à l'écran.

Il compare ensuite où se trouve la souris. Tant que la souris se trouve dans la plage de variables nouvellement créée, elle renvoie true.

Remarque:

Il est judicieux de positionner l'élément parent RELATIVE. Si vous avez un élément psuedo positionné en absolu, cette fonction ne fonctionnera que si elle est positionnée en fonction des dimensions du parent (donc le parent doit être relatif ... peut-être collant ou fixe fonctionnerait aussi ... je ne sais pas).

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

  };      

}

Soutien:

Je ne sais pas .... on dirait que c'est stupide et aime parfois retourner auto comme valeur calculée parfois. IL SEMBLE BIEN FONCTIONNER DANS TOUS LES NAVIGATEURS SI LES DIMENSIONS SONT RÉGLÉES EN CSS. Alors ... réglez votre hauteur et votre largeur sur vos éléments de pédale et déplacez-les uniquement en haut et à gauche. Je recommande de l'utiliser sur des choses qui ne vous conviennent pas. Comme une animation ou quelque chose. Chrome fonctionne ... comme d'habitude.


la source
eventest transmis via une fonction de gestionnaire d'événements chaque fois qu'un événement est déclenché. Comme cliquer ou taper. Lorsque nous utilisons la .click()fonction, nous attendons l'événement click. Nous pourrions spécifier et dire .click(function(e){...})que l'événement doit être, emais il est également acceptable de simplement l'utiliser event.
3
psEUdo, pas psUEdo!
biziclop
Le code ci-dessus ne fonctionne pas, car il eventn'est pas défini.
Sebastian Zartner
@SebastianZartner - L'événement est un mot-clé. C'est lorsque l'utilisateur fait quelque chose. J'ai littéralement expliqué ces deux commentaires au-dessus de votre commentaire. Lorsque l'utilisateur interagit avec la page, un événement est déclenché (dans la plupart des cas). L'événement est un mot clé provenant de l'écouteur d'événement ".click ()". Si un développeur souhaite utiliser un nom différent pour l'événement, il peut le configurer dans l'écouteur d'événement comme ".click (e)" qui est une façon plus courante de le voir. CEPENDANT .... même si j'ai un lien qui fonctionne ci-dessus, j'en inclurai également un qui est plus évident et ne se connecte pas à la console mais à la page pour les nécessiteux.
J'aurais dû mentionner que cela ne fonctionne pas dans Firefox, car il eventn'est pas défini. Cela fonctionne cependant dans Chrome et Edge.
Sebastian Zartner
2

Ajoutez une condition dans l'événement Click pour restreindre la zone cliquable.

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

Ceci est la réponse éditée par Fasoeu avec les derniers CSS3 et JS ES6

Démo éditée sans utiliser JQuery.

Exemple de code le plus court:

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

Explication:

pointer-eventscontrôler la détection des événements, supprimer les événements de réception de la cible, mais continuer à recevoir des pseudo-éléments permet de cliquer sur ::beforeet ::aftervous saurez toujours sur quoi vous cliquez sur le pseudo-élément, mais si vous devez toujours cliquer, vous mettez tout le contenu dans un élément imbriqué ( spandans l'exemple), mais parce que nous ne voulons pas appliquer de styles supplémentaires, display: contents;devenez une solution très pratique et prise en charge par la plupart des navigateurs . pointer-events: none;comme déjà mentionné dans le message d'origine également largement pris en charge .

La partie JavaScript est également largement utilisée Array.fromet for...ofn'est cependant pas nécessaire à utiliser dans le code.

XCanG
la source
0

Aucune de ces réponses n'est fiable et la mienne ne sera pas beaucoup plus fiable.

Mises en garde de côté, si vous entrez dans le scénario chanceux où l'élément sur lequel vous essayez de cliquer n'a pas de remplissage (de sorte que tout l'espace "intérieur" de l'élément est complètement couvert par des sous-éléments), alors vous peut comparer la cible de l'événement click au conteneur lui-même. S'il correspond, cela signifie que vous avez cliqué sur un élément: avant ou: après.

Évidemment, cela ne serait pas possible avec les deux types (avant et après), mais je l'ai implémenté comme un hack / speed fix et cela fonctionne très bien, sans un tas de vérification de position, ce qui peut être inexact en fonction d'environ un million de facteurs différents .

dudewad
la source
-2

Non, mais tu peux faire comme ça

Dans le fichier html ajoutez cette section

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

En CSS, vous pouvez faire comme ça

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

} J'espère que cela vous aidera

Gopal Bogati
la source