Como adiciono um manipulador de eventos onClick simples a um elemento de tela?

128

Sou um programador Java experiente, mas estou vendo algumas coisas sobre JavaScript / HTML5 pela primeira vez em cerca de uma década. Estou completamente perplexo com o que deveria ser a coisa mais simples de todas.

Como exemplo, eu só queria desenhar algo e adicionar um manipulador de eventos a ele. Tenho certeza de que estou fazendo algo estúpido, mas pesquisei por toda parte e nada que seja sugerido (por exemplo, a resposta a esta pergunta: Adicionar propriedade onclick para inserir com JavaScript ) funciona. Estou usando o Firefox 10.0.1. Meu código segue. Você verá várias linhas comentadas e no final de cada uma é uma descrição do que (ou do que não acontece).

Qual é a sintaxe correta aqui? Eu estou ficando louco!

<html>
<body>
    <canvas id="myCanvas" width="300" height="150"/>
    <script language="JavaScript">
        var elem = document.getElementById('myCanvas');
        // elem.onClick = alert("hello world");  - displays alert without clicking
        // elem.onClick = alert('hello world');  - displays alert without clicking
        // elem.onClick = "alert('hello world!')";  - does nothing, even with clicking
        // elem.onClick = function() { alert('hello world!'); };  - does nothing
        // elem.onClick = function() { alert("hello world!"); };  - does nothing
        var context = elem.getContext('2d');
        context.fillStyle = '#05EFFF';
        context.fillRect(0, 0, 150, 100);
    </script>

</body>

Jer
fonte
8
Use em onclickvez deonClick
noob
1
Para explicar por que essas tentativas não funcionaram ... Os dois primeiros comentários exibem um alerta imediatamente porque você está ligando alert()diretamente <script>, em vez de definir uma função que será chamada alert(). O resto não faz nada por causa da capitalização de onclick.
LarsH
Você pode usar esta lib jsfiddle.net/user/zlatnaspirala/fiddles , veja bitbucket.org/nikola_l/visual-js . Você terá muitos recursos +
Nikola Lukic

Respostas:

223

Quando você desenha para um canvaselemento, está simplesmente desenhando um bitmap no modo imediato .

Os elementos (formas, linhas, imagens) desenhados não têm representação além dos pixels que usam e de suas cores.

Portanto, para obter um evento de clique em um canvas elemento (forma), é necessário capturar eventos de clique no canvaselemento HTML e usar um pouco de matemática para determinar qual elemento foi clicado, desde que você esteja armazenando a largura / altura e deslocamento x / y dos elementos .

Para adicionar um clickevento ao seu canvaselemento, use ...

canvas.addEventListener('click', function() { }, false);

Para determinar qual elemento foi clicado ...

var elem = document.getElementById('myCanvas'),
    elemLeft = elem.offsetLeft + elem.clientLeft,
    elemTop = elem.offsetTop + elem.clientTop,
    context = elem.getContext('2d'),
    elements = [];

// Add event listener for `click` events.
elem.addEventListener('click', function(event) {
    var x = event.pageX - elemLeft,
        y = event.pageY - elemTop;

    // Collision detection between clicked offset and element.
    elements.forEach(function(element) {
        if (y > element.top && y < element.top + element.height 
            && x > element.left && x < element.left + element.width) {
            alert('clicked an element');
        }
    });

}, false);

// Add element.
elements.push({
    colour: '#05EFFF',
    width: 150,
    height: 100,
    top: 20,
    left: 15
});

// Render elements.
elements.forEach(function(element) {
    context.fillStyle = element.colour;
    context.fillRect(element.left, element.top, element.width, element.height);
});​

jsFiddle .

Esse código anexa um clickevento ao canvaselemento e envia uma forma (chamada elementno meu código) para uma elementsmatriz. Você pode adicionar quantas quiser aqui.

O objetivo de criar uma matriz de objetos é para que possamos consultar suas propriedades posteriormente. Depois que todos os elementos foram inseridos na matriz, fazemos um loop e renderizamos cada um deles com base em suas propriedades.

Quando o clickevento é acionado, o código percorre os elementos e determina se o clique foi sobre algum dos elementos da elementsmatriz. Nesse caso, ele dispara um alert(), que poderia ser facilmente modificado para fazer algo como remover o item da matriz; nesse caso, você precisaria de uma função de renderização separada para atualizar o canvas.


Para ser completo, por que suas tentativas não funcionaram ...

elem.onClick = alert("hello world"); // displays alert without clicking

Isso está atribuindo o valor de retorno de alert()à onClickpropriedade de elem. Está chamando imediatamente o alert().

elem.onClick = alert('hello world');  // displays alert without clicking

Em JavaScript, 'e "são semanticamente idênticos, o lexer provavelmente usa ['"]para aspas.

elem.onClick = "alert('hello world!')"; // does nothing, even with clicking

Você está atribuindo uma sequência à onClickpropriedade de elem.

elem.onClick = function() { alert('hello world!'); }; // does nothing

JavaScript diferencia maiúsculas de minúsculas. A onclickpropriedade é o método arcaico de anexar manipuladores de eventos. Ele permite apenas que um evento seja anexado à propriedade e o evento pode ser perdido ao serializar o HTML.

elem.onClick = function() { alert("hello world!"); }; // does nothing

Mais uma vez ' === ".

alex
fonte
Hum ... perdoe-me se estou entendendo mal - mas não estou capturando eventos de clique no elemento da tela? Não sei ao certo o que você quer dizer com qual elemento foi clicado. Há apenas um elemento nesta página, certo?
26412 Jer
Ele quis dizer "qual elemento da tela" - ou seja, qual "forma", por assim dizer.
Jovan Perovic
1
Muito obrigado - embora eu não esteja totalmente claro sobre o último. Estou seguindo o exemplo aqui: developer.mozilla.org/en/DOM/element.onclick (usando a função anônima que eles descrevem) e funciona, mesmo quando altero o intervalo para uma tela. Portanto, não será difícil para mim transformar lentamente o exemplo deles no meu para ver o que estou fazendo de errado. Obrigado pela resposta detalhada! A explicação de por que minhas várias tentativas estavam erradas foi especialmente útil.
26412 Jer
1
@ Jer Não se preocupe, se você tiver mais perguntas, sinta-se à vontade para postar e enviar ping para mim no Twitter, @alexdickson .
26412 alex
1
@HashRocketSyntax Ele deve funcionar bem, basta atualizar as posições dos seus objetos na memória e usar essas definições para tornar a partir
alex
4

Provavelmente muito tarde para a resposta, mas acabei de ler isso enquanto me preparava para o 70-480exame e achei que funcionava -

var elem = document.getElementById('myCanvas');
elem.onclick = function() { alert("hello world"); }

Observe o evento como em onclickvez de onClick.

Exemplo de JS Bin .

Soham Dasgupta
fonte
3

Recomendo o seguinte artigo: Detecção de região de ocorrência para tela HTML5 e como ouvir eventos de clique em formas de tela que passam por várias situações.

No entanto, ele não cobre a addHitRegionAPI, que deve ser a melhor maneira (o uso de funções matemáticas e / ou comparações é bastante suscetível a erros). Essa abordagem é detalhada em developer.mozilla

RUser4512
fonte
"[ addHitRegion] é obsoleto. Embora ainda possa funcionar em alguns navegadores, seu uso é desencorajado, pois pode ser removido a qualquer momento. Tente evitar usá-lo." - no link dev.moz que você inclui, para salvar um clique de outras pessoas.
Chris
2

Como alternativa à resposta de alex:

Você pode usar um desenho SVG em vez de um desenho do Canvas. Lá você pode adicionar eventos diretamente aos objetos DOM desenhados.

veja por exemplo:

Tornar um objeto de imagem svg clicável com onclick, evitando posicionamento absoluto

Goswin
fonte
1
desculpe pelo entusiasmo, é que eu tinha um problema para o qual usar SVG é a solução óbvia, e eu não tinha pensado nisso até o seu comentário ^^
Elias Dorneles
1

Você também pode colocar elementos DOM, como divno topo da tela, que representariam seus elementos da tela e seriam posicionados da mesma maneira.

Agora você pode anexar ouvintes de eventos a essas divs e executar as ações necessárias.

Sergei Basharov
fonte
1

Como outra alternativa barata em uma tela um pouco estática, o uso de um elemento img de sobreposição com uma definição de mapa de uso é rápido e sujo. Funciona especialmente bem em elementos de tela baseados em polígonos, como um gráfico de pizza.

TadLewis
fonte
0

Alex Answer é muito legal, mas ao usar a rotação de contexto, pode ser difícil rastrear as coordenadas x, y, então eu fiz uma demonstração mostrando como acompanhar isso.

Basicamente, estou usando esta função e fornecendo o ângulo e a distância percorrida naquele anjo antes de desenhar um objeto.

function rotCor(angle, length){
    var cos = Math.cos(angle);
    var sin = Math.sin(angle);

    var newx = length*cos;
    var newy = length*sin;

    return {
        x : newx,
        y : newy
    };
}
Imran Bughio
fonte