Detectar apenas evento de clique no pseudo-elemento

213

Meu código é:

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

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

Por favor, veja este violino: http://jsfiddle.net/ZWw3Z/5/

Gostaria de acionar um evento de clique apenas no pseudoelemento (o bit vermelho). Ou seja, não quero que o evento click seja acionado no bit azul.

Randomblue
fonte
1
Que tal verificar a posição clicada? stackoverflow.com/questions/3234977/…
Leia minha solução postada anteriormente aqui .
Amr
Possivelmente uma boa solução alternativa AQUI .
Reza Mamun

Respostas:

218

Isso não é possível; os pseudoelementos não fazem parte do DOM, portanto, você não pode vincular nenhum evento diretamente a eles, apenas os elementos pai.

Se você precisar ter um manipulador de cliques apenas na região vermelha, precisará criar um elemento filho, como a span, colocá-lo logo após a <p>tag de abertura , aplicar estilos ao p spaninvés de p:beforee vincular a ele.

BoltClock
fonte
108
Parece que em navegadores modernos este é tipo de possível com diferentes pointer-eventsvalores para o próprio e seu elemento pseudo elemento: jsfiddle.net/ZWw3Z/70
Ilya Streltsyn
5
@Ilya Streltsyn incrível - não é a resposta 'correta' (não iria funcionar se você também precisava clique no elemento), mas funciona brilhantemente para um 'botão de fechar' no divs
ROZZA
pointer-eventsnão parece fazer o truque para o IE9, de acordo com o jsfiddel publicado por Ilya.
user393274
@ user393274: O IE9 não suporta eventos de ponteiro fora do SVG.
BoltClock
1
@theyuv não, nessa página é possível clicar em toda a linha, tanto à esquerda quanto à direita no link. Somente o link em si tem um manipulador de cliques diferente, portanto, clicar nele não aciona a subárvore dobrável / desdobrável.
Ilya Streltsyn
131

Na verdade, é possível. Você pode verificar se a posição clicada estava fora do elemento, pois isso só acontecerá se ::beforeou ::afterfoi clicada.

Este exemplo verifica apenas o elemento à direita, mas isso deve funcionar no seu 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
fonte
1
Não funciona para mim, o texto fica destacado quando clico em qualquer um dos dois. Usando o Firefox, se for importante (não deveria).
quer
2
para o firefox: jsfiddle.net/wC2p7/16 . Se trabalhar com elementos aninhados, pode ser necessário para calcular as compensações aninhados conforme o caso: (por exemplo, jQuery $ (e.target) .Offset () esquerda..)
Bebbi
1
Homem incrível, graças, funciona. Mas no Firefox e em outros navegadores compatíveis com os padrões, para testar se o mouse :afteré necessário, verifique se if (e.clientX > span.offsetLeft + span.offsetHeight)esses navegadores não possuem a e.offsetXpropriedade. Fiddle: jsfiddle.net/Noitidart/wC2p7/18
Noitidart
5
Ainda mais legal seria se jQuery decidiu apoiar $('a:after').on('click', fn), mas eu realmente não vejo isso acontecendo: p
Linus Unnebäck
25
Na verdade, isso não funcionará se ::beforeou ::afterestiver posicionado dentro do elemento.
laconbass
74

Nos navegadores modernos, você pode tentar com a propriedade css pointer-events (mas leva à impossibilidade de detectar eventos do mouse no nó pai):

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

Quando o destino do evento é o seu elemento "p", você sabe que é o seu "p: antes".

Se você ainda precisar detectar eventos de mouse no p principal, considere a possibilidade de modificar sua estrutura HTML. Você pode adicionar uma tag de extensão e o seguinte estilo:

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

Os destinos do evento agora são os elementos "span" e "p: before".

Exemplo sem jquery: http://jsfiddle.net/2nsptvcu/

Exemplo com jquery: http://jsfiddle.net/0vygmnnb/

Aqui está a lista de navegadores que suportam eventos-ponteiro: http://caniuse.com/#feat=pointer-events

Fasoeu
fonte
Eu não sei por que o nó pai é. Você está dizendo que, se eu clicar no evento em algo como .box: antes, o então .box não poderá detectar nenhum clique?
mais venerável senhor
Isso é parcialmente verdadeiro: se o seu CSS contiver algo como .box {eventos-ponteiro: nenhum;} .box: before {eventos-ponteiro: auto;}, o único elemento que ainda suporta eventos é p: before. Lembre-se de qualquer maneira que js retornará o elemento .box principal, já que .box: before realmente não mora no DOM.
Fasoeu
9

Minha resposta funcionará para quem quiser clicar em uma área definitiva da página. Isso funcionou para mim no meu absolutamente posicionado: depois

Graças a este artigo , percebi (com jQuery) que posso usar e.pageYe, em e.pageXvez de me preocupar e.offsetY/Xe e.clientY/Xemitir problemas entre navegadores.

Através da minha tentativa e erro, comecei a usar as coordenadas do mouse clientX e clientY no objeto de evento jQuery. Essas coordenadas me deram o deslocamento X e Y do mouse em relação ao canto superior esquerdo da porta de visualização do navegador. Enquanto lia o Guia de Referência do jQuery 1.4 de Karl Swedberg e Jonathan Chaffer, no entanto, vi que eles frequentemente se referiam às coordenadas pageX e pageY. Depois de verificar a documentação atualizada do jQuery, vi que essas eram as coordenadas padronizadas pelo jQuery; e vi que eles me deram o deslocamento X e Y do mouse em relação ao documento inteiro (não apenas à porta de visualização).

Gostei dessa event.pageYideia, porque sempre seria a mesma, pois era relativa ao documento . Posso compará-lo ao meu elemento pai my: after usando offset (), que retorna seu X e Y também em relação ao documento.

Portanto, posso criar um intervalo de região "clicável" em toda a página que nunca muda.


Aqui está minha demo no codepen .


ou se tiver preguiça de codificar, aqui está o JS:

* Eu só me importei com os valores Y do meu exemplo.

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

Isso funciona para mim:

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

Resposta curta:

Eu fiz isso. Eu escrevi uma função para uso dinâmico para todas as pessoas pequenas por aí ...

Exemplo de trabalho que é exibido na página

Exemplo de Trabalho Registrando no Console

Resposta longa:

... Ainda fiz isso.

Levei um tempo para fazê-lo, pois um elemento psuedo não está realmente na página. Embora algumas das respostas acima funcionem em ALGUNS cenários, TODAS elas não são dinâmicas e funcionam em um cenário no qual um elemento é inesperado em tamanho e posição (como elementos posicionados absolutos sobrepondo uma parte do elemento pai). O meu não.

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;

});

Como funciona:

Ele agarra as posições de altura, largura, superior e esquerda (com base na posição da borda da janela) do elemento pai e agarra as posições de altura, largura, parte superior e esquerda (com base na borda do contêiner pai ) e compara esses valores para determinar onde o elemento psuedo está na tela.

Em seguida, ele compara onde está o mouse. Enquanto o mouse estiver no intervalo de variáveis ​​recém-criado, ele retornará verdadeiro.

Nota:

É aconselhável colocar o elemento pai em RELATIVO. Se você tiver um elemento psuedo posicionado absoluto, essa função funcionará apenas se for posicionada com base nas dimensões do pai (portanto, o pai deve ser relativo ... talvez fixo ou fixo funcione também ... eu não sei).

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

  };      

}

Apoio, suporte:

Eu não sei .... parece, ou seja, é burro e gosta de retornar auto como um valor calculado às vezes. Parece funcionar bem em todos os navegadores, se as dimensões estiverem definidas em CSS. Então ... defina sua altura e largura nos seus elementos psuedo e mova-os apenas com a parte superior e a esquerda. Eu recomendo usá-lo em coisas que você não concorda. Como uma animação ou algo assim. Chrome funciona ... como de costume.


fonte
eventé passado por uma função de manipulador de eventos toda vez que um evento é disparado. Como clicar ou digitar. Quando usamos a .click()função, estamos aguardando o evento click. Poderíamos especificar e dizer .click(function(e){...})para tornar o evento, emas também é aceitável usar apenas event.
3
psEUdo, não psUEdo!
biziclop
O código acima não funciona, porque eventé indefinido.
Sebastian Zartner 7/01/19
@SebastianZartner - Event é uma palavra-chave. É quando o usuário faz alguma coisa. Eu literalmente expliquei esses dois comentários acima do seu comentário. Quando o usuário interage com a página, um evento é disparado (na maioria dos casos). Evento é uma palavra-chave proveniente do ouvinte de eventos ".click ()". Se um desenvolvedor quiser usar um nome diferente para o evento, poderá configurá-lo no ouvinte do evento como ".click (e)", que é a maneira mais comum de vê-lo. NO ENTANTO .... embora eu tenha um link funcionando acima, também incluirei um que seja mais óbvio e não faça logon no console, mas na página dos necessitados.
Eu deveria ter mencionado que ele não funciona no Firefox, porque não eventé definido. Porém, ele funciona no Chrome e Edge.
Sebastian Zartner
2

Adicionar condição no evento Click para restringir a área clicável.

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

Esta é uma resposta editada por Fasoeu com as últimas CSS3 e JS ES6

Demonstração editada sem usar o JQuery.

Exemplo mais curto 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);
};

Explicação:

pointer-eventsdetecção de eventos de controle, removendo receber eventos de alvo, mas continue a receber a partir de pseudo-elementos tornam possível clicar ::beforee ::aftere você sempre saberá o que você está clicando no pseudo-elemento, no entanto, se você ainda precisa clicar, você coloca todos os conteúdos no elemento aninhado ( spanno exemplo), mas como não queremos aplicar estilos adicionais, display: contents;torne-se uma solução muito útil e suportada pela maioria dos navegadores . pointer-events: none;como já mencionado no post original, também amplamente suportado .

A parte JavaScript também usou amplamente o suporte Array.frome for...of, no entanto, não é necessário usar o código.

XCanG
fonte
0

Nenhuma dessas respostas é confiável e a minha não será muito mais confiável.

Advertências à parte, se você entrar no cenário de sorte em que o elemento que está tentando clicar não possui preenchimento (de modo que todo o espaço "interno" do elemento seja completamente coberto por subelementos), então você pode verificar o destino do evento click no próprio contêiner. Se corresponder, significa que você clicou no elemento: before ou: after.

Obviamente, isso não seria viável nos dois tipos (antes e depois), mas eu o implementei como correção de velocidade / hack e está funcionando muito bem, sem muitas verificações de posição, que podem ser imprecisas dependendo de um milhão de fatores diferentes .

dudewad
fonte
-2

Não, mas você pode fazer assim

No arquivo html, adicione esta seção

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

Em css você pode fazer assim

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

} Espero que ajude você

Gopal Bogati
fonte