Solo detectar evento de clic en pseudo-elemento

213

Mi código es:

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

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

Por favor vea este violín: http://jsfiddle.net/ZWw3Z/5/

Me gustaría activar un evento de clic solo en el pseudo-elemento (el bit rojo). Es decir, no quiero que el evento click se active en el bit azul.

Randomblue
fuente
1
¿Qué hay de comprobar pos clics? stackoverflow.com/questions/3234977/…
Lea mi solución publicada anteriormente aquí .
Amr
Posiblemente una buena solución aquí .
Reza Mamun

Respuestas:

218

Esto no es posible; los pseudo-elementos no son parte del DOM en absoluto, por lo que no puede vincular ningún evento directamente a ellos, solo puede vincular a sus elementos principales.

Si debe tener un controlador de clics solo en la región roja, debe crear un elemento secundario, como un span, colocarlo justo después de la <p>etiqueta de apertura , aplicar estilos en p spanlugar de p:beforey vincularlo.

BoltClock
fuente
108
Parece que en los navegadores modernos esto es posible con diferentes pointer-eventsvalores para el elemento en sí y su pseudo elemento: jsfiddle.net/ZWw3Z/70
Ilya Streltsyn
55
@Ilya Streltsyn increíble: no es la respuesta 'correcta' (no funcionaría si también necesitaras hacer clic en el elemento) pero funciona de manera brillante para un 'botón de cierre' en divs
RozzA
pointer-eventsno parece hacer el truco para IE9, según el jsfiddel que publicó Ilya.
user393274
@ user393274: IE9 no admite eventos de puntero fuera de SVG.
BoltClock
1
@theyuv no, en esa página se puede hacer clic en toda la línea, tanto a la izquierda como a la derecha desde el enlace. Solo el enlace en sí tiene un controlador de clics diferente, por lo que al hacer clic no se activa el subárbol de plegado / desplegado.
Ilya Streltsyn
131

En realidad, es posible. Puede verificar si la posición en la que se hizo clic estaba fuera del elemento, ya que esto solo sucederá si se hizo clic ::beforeo ::afterse hizo clic en ella.

Este ejemplo solo verifica el elemento a la derecha, pero eso debería funcionar en su caso.

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
fuente
1
No me funciona, el texto se resalta cuando hago clic en cualquiera de los dos. Usando Firefox, si es importante (no debería).
Flater
2
para firefox: jsfiddle.net/wC2p7/16 . Si trabaja con elementos anidados, es posible que necesite calcular las compensaciones anidadas según corresponda (por ejemplo, jQuery: $ (e.target) .offset ().
Left
1
Hombre impresionante, gracias, funciona. Pero en Firefox y otros navegadores que cumplen con los estándares, para probar si pasa el mouse :after, debe verificar if (e.clientX > span.offsetLeft + span.offsetHeight)ya que estos navegadores no tienen la e.offsetXpropiedad. Fiddle: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
55
Aún más genial sería si jQuery decidiera apoyar, $('a:after').on('click', fn)pero realmente no veo que eso suceda: p
Linus Unnebäck
25
En realidad, esto no funcionará si ::beforeo ::afterestá posicionado dentro del elemento.
laconbass
74

En los navegadores modernos , puede probar con la propiedad css de puntero-eventos (pero conduce a la imposibilidad de detectar eventos del mouse en el nodo primario):

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

Cuando el objetivo del evento es su elemento "p", sabe que es su "p: antes".

Si aún necesita detectar eventos del mouse en la p principal, puede considerar la posibilidad de modificar su estructura HTML. Puede agregar una etiqueta span y el siguiente estilo:

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

Los objetivos del evento ahora son los elementos "span" y "p: before".

Ejemplo sin jquery: http://jsfiddle.net/2nsptvcu/

Ejemplo con jquery: http://jsfiddle.net/0vygmnnb/

Aquí está la lista de navegadores que admiten eventos de puntero: http://caniuse.com/#feat=pointer-events

Fasoeu
fuente
No sé por qué es el nodo padre. ¿Estás diciendo que si el evento click en algo como .box: antes, entonces .box no puede detectar ningún clic?
Señor más venerable
Eso es parcialmente cierto: si su CSS contiene algo como .box {pointer-events: none;} .box: before {pointer-events: auto;} entonces el único elemento que aún admite eventos es p: before. Recuerde de todos modos que js le devolverá el elemento .box principal ya que .box: before realmente no vive en The DOM.
Fasoeu
9

Mi respuesta funcionará para cualquiera que quiera hacer clic en un área definitiva de la página. Esto funcionó para mí en mi posición absoluta: después

Gracias a este artículo , me di cuenta (con jQuery) que puedo usar e.pageYy en e.pageXlugar de preocuparme e.offsetY/Xy e.clientY/Xemitir problemas entre los navegadores.

A través de mi prueba y error, comencé a usar las coordenadas del mouse clientX y clientY en el objeto de evento jQuery. Estas coordenadas me dieron el desplazamiento X e Y del mouse con respecto a la esquina superior izquierda del puerto de visualización del navegador. Sin embargo, mientras leía la Guía de referencia jQuery 1.4 de Karl Swedberg y Jonathan Chaffer, vi que a menudo se referían a las coordenadas pageX y pageY. Después de verificar la documentación actualizada de jQuery, vi que estas eran las coordenadas estandarizadas por jQuery; y, vi que me dieron el desplazamiento X e Y del mouse con respecto a todo el documento (no solo el puerto de vista).

Me gustó esta event.pageYidea porque siempre sería la misma, ya que era relativa al documento . Puedo compararlo con el elemento principal de: after usando offset (), que devuelve sus X e Y también en relación con el documento.

Por lo tanto, puedo crear un rango de región "cliqueable" en toda la página que nunca cambia.


Aquí está mi demo en codepen .


o si es demasiado vago para codepen, aquí está el JS:

* Solo me importaban los valores Y para mi ejemplo.

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

Esto funciona para mi:

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

Respuesta corta:

Lo hice. Escribí una función para uso dinámico para todas las personas pequeñas ...

Ejemplo de trabajo que se muestra en la página

Ejemplo de trabajo de inicio de sesión en la consola

Respuesta larga:

... Todavía lo hice.

Me tomó un tiempo hacerlo, ya que un elemento psuedo no está realmente en la página. Si bien algunas de las respuestas anteriores funcionan en ALGUNOS escenarios, TODOS no pueden ser dinámicos y funcionan en un escenario en el que un elemento tiene un tamaño y una posición inesperados (como elementos de posición absoluta que se superponen a una parte del elemento principal). La mía no.

Uso:

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

});

Cómo funciona:

Toma las posiciones de alto, ancho, superior e izquierda (según la posición alejada del borde de la ventana) del elemento principal y toma las posiciones de alto, ancho, superior e izquierda (según el borde del contenedor principal) ) y compara esos valores para determinar dónde está el elemento psuedo en la pantalla.

Luego compara dónde está el mouse. Mientras el mouse esté en el rango de variables recién creado, devuelve verdadero.

Nota:

Es aconsejable colocar el elemento padre en una posición RELATIVA. Si tiene un elemento psuedo posicionado de forma absoluta, esta función solo funcionará si se posiciona en función de las dimensiones del padre (por lo que el padre tiene que ser relativo ... tal vez pegajoso o fijo también funcionaría ...

Código:

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

  };      

}

Apoyo:

No sé ... parece que es tonto y a veces le gusta devolver auto como un valor calculado. Parece funcionar bien en todos los navegadores si las dimensiones se establecen en CSS. Entonces ... establezca su altura y ancho en sus elementos psuedo y solo muévalos con la parte superior e izquierda. Recomiendo usarlo en cosas en las que está de acuerdo con que no funcione. Como una animación o algo así. Chrome funciona ... como siempre.


fuente
eventse pasa a través de una función de controlador de eventos cada vez que se dispara un evento. Como hacer clic o escribir. Cuando usamos la .click()función, estamos esperando el evento de clic. Podríamos especificar y decir .click(function(e){...})que el evento sea, epero también es aceptable usarlo event.
3
psEUdo, no psUEdo!
biziclop
El código anterior no funciona porque eventno está definido.
Sebastian Zartner el
@SebastianZartner: el evento es una palabra clave. Es cuando el usuario hace algo. Literalmente expliqué estos dos comentarios sobre tu comentario. Cuando el usuario interactúa con la página, se activa un evento (en la mayoría de los casos). Evento es una palabra clave que proviene del detector de eventos ".click ()". Si un desarrollador quiere usar un nombre diferente para el evento, puede configurarlo en el escucha de eventos como ".click (e)", que es una forma más común de verlo. SIN EMBARGO ... a pesar de que tengo un enlace que funciona arriba, también incluiré uno que sea más obvio y que no inicie sesión en la consola sino en la página para los necesitados.
Debería haber mencionado que no funciona en Firefox, porque no eventestá definido. Sin embargo, funciona en Chrome y Edge.
Sebastian Zartner
2

Agregar condición en el evento Click para restringir el área en la que se puede hacer clic.

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

MANIFESTACIÓN

Abdennour TOUMI
fuente
1

Esta es la respuesta editada por Fasoeu con el último CSS3 y JS ES6

Demo editada sin usar JQuery.

El ejemplo más corto de código:

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

Explicación:

pointer-eventscontrola la detección de eventos, elimina los eventos de recepción del objetivo, pero sigue recibiendo de pseudoelementos que hacen posible hacer clic ::beforey ::aftersiempre sabrás en qué estás haciendo clic en el pseudo-elemento, sin embargo, si aún necesitas hacer clic, pones todo el contenido en el elemento anidado ( spanen el ejemplo), pero debido a que no queremos aplicar ningún estilo adicional, display: contents;conviértase en una solución muy útil y es compatible con la mayoría de los navegadores . pointer-events: none;como ya se mencionó en la publicación original, también es ampliamente compatible .

La parte de JavaScript también se usa ampliamente Array.fromy for...of, sin embargo, no es necesario usarla en el código.

XCanG
fuente
0

Ninguna de estas respuestas es confiable, y la mía no será mucho más confiable.

Dejando de lado las advertencias, si ingresa al escenario de la suerte en el que el elemento en el que intenta hacer clic no tiene relleno (de modo que todo el espacio "interno" del elemento esté completamente cubierto por subelementos), entonces usted puede verificar el destino del evento de clic en el contenedor mismo. Si coincide, eso significa que ha hecho clic en un elemento: before o: after.

Obviamente, esto no sería factible con ambos tipos (antes y después), sin embargo, lo he implementado como un truco / corrección de velocidad y está funcionando muy bien, sin un montón de verificación de posición, que puede ser inexacto dependiendo de aproximadamente un millón de factores diferentes .

amigo
fuente
-2

No, pero puedes hacer así

En el archivo html agregue esta sección

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

En css puedes hacer así

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

} Espero que te ayude

Gopal Bogati
fuente