W3docs

Coordenadas em JavaScript

Aprenda os dois sistemas de coordenadas do navegador, clientX/pageX, getBoundingClientRect() e elementFromPoint() para posicionar elementos.

Coordenadas são valores numéricos que descrevem uma posição na página. Quase todo recurso interativo — arrastar, desenhar, tooltips, menus pop-up, detecção de colisão com o cursor — se resume a ler uma coordenada e posicionar ou comparar algo com ela.

O problema é que o navegador expõe dois sistemas de coordenadas diferentes, e confundi-los é a fonte mais comum de bugs do tipo "meu elemento pula para o lugar errado". Este capítulo explica os dois sistemas, como converter entre eles e os principais métodos do DOM (getBoundingClientRect() e elementFromPoint()) que você usa para ler a posição de elementos.

Os dois sistemas de coordenadas

Há dois referenciais que você precisa manter bem separados:

  • Coordenadas do viewport (relativas à janela) — medidas a partir do canto superior esquerdo da parte visível da página. Elas não mudam quando você rola a página. Um ponto no canto superior esquerdo do que você está vendo é sempre (0, 0). Propriedades: clientX / clientY.
  • Coordenadas do documento (relativas à página) — medidas a partir do canto superior esquerdo do documento inteiro, incluindo a parte que está fora da área visível por causa da rolagem. Elas aumentam conforme você rola para baixo. Propriedades: pageX / pageY.

A relação entre elas é simplesmente o deslocamento da rolagem:

pageX = clientX + window.scrollX;
pageY = clientY + window.scrollY;

Quando a página não está rolada, window.scrollX e window.scrollY valem ambos 0, então os dois sistemas fornecem números idênticos — e é exatamente por isso que os bugs de rolagem ficam escondidos até que alguém role a página.

Use o viewport (clientX/Y) quando…Use o documento (pageX/Y) quando…
Posicionar um elemento position: fixedPosicionar um elemento position: absolute
Fazer hit-testing do que está atualmente na telaArmazenar a posição de um clique para recriá-la depois
Trabalhar com getBoundingClientRect()Desenhar em um canvas alto e rolável

Veja Tamanhos da janela e rolagem em JavaScript para saber como window.scrollX/Y e o tamanho do viewport são medidos, e Rolagem em JavaScript para mover a posição de rolagem via código.

Lendo coordenadas de um evento do mouse

Todo evento do mouse traz os dois sistemas de coordenadas. Para o modelo completo de eventos, consulte Fundamentos dos eventos do mouse.

Coordenadas do viewport: clientX / clientY

event.clientX e event.clientY fornecem a posição do ponteiro relativa ao viewport, independentemente da rolagem. O exemplo abaixo está dentro de uma página alta para que você possa rolar e confirmar que os números permanecem ancorados à área visível:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Client-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in the grey area to see Client-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showCoords(event) {
            alert("Client-Side X: " + event.clientX + ", Y: " + event.clientY);
        }
        container.addEventListener('click', showCoords);
    </script>
</body>
</html>

Coordenadas do documento: pageX / pageY

event.pageX e event.pageY fornecem a posição do ponteiro relativa ao documento inteiro, portanto já incluem o deslocamento da rolagem. Elas são padronizadas e amplamente suportadas — não há necessidade de calculá-las manualmente. O exemplo abaixo as mostra ao lado do cálculo manual clientX + window.scrollX, para que você confirme que os dois resultados coincidem:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Page-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in this area to see Page-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showPageCoords(event) {
            // pageX/pageY already include scroll; the manual version must match
            const manualX = event.clientX + window.scrollX;
            const manualY = event.clientY + window.scrollY;
            alert("pageX/Y: " + event.pageX + ", " + event.pageY +
                  "\nclientX+scrollX: " + manualX + ", " + manualY);
        }
        container.addEventListener('click', showPageCoords);
    </script>
</body>
</html>

Coordenadas da tela: screenX / screenY

event.screenX e event.screenY medem o ponteiro em relação à tela física inteira, e não apenas à janela do navegador. São úteis para acompanhar o cursor entre vários monitores ou para posicionar um popup de window.open(), mas, para trabalhar dentro da página, quase sempre você vai querer clientX/Y ou pageX/Y.

Lendo a posição de um elemento: getBoundingClientRect()

Para descobrir onde um elemento está (em vez de onde o mouse está), chame getBoundingClientRect(). Ele retorna um DOMRect cujas propriedades top, right, bottom, left, width e height estão todas em coordenadas do viewport — o mesmo referencial de clientX/Y.

const rect = element.getBoundingClientRect();
// rect.left, rect.top   → top-left corner, relative to the viewport
// rect.width, rect.height → rendered size including borders/padding

Como o retângulo é relativo ao viewport, ele muda conforme você rola a página. Para obter a posição do elemento em coordenadas do documento, some o deslocamento da rolagem:

function getDocumentCoords(el) {
  const rect = el.getBoundingClientRect();
  return {
    top: rect.top + window.scrollY,
    left: rect.left + window.scrollX,
  };
}

Esse par — ler com getBoundingClientRect() e somar scrollX/Y quando precisar das coordenadas da página — é a conversão que você vai usar o tempo todo.

Encontrando o elemento em um ponto: elementFromPoint()

A pergunta inversa — "o que está sob estas coordenadas?" — é respondida por document.elementFromPoint(x, y), que recebe coordenadas do viewport (equivalentes a clientX/Y) e retorna o elemento mais ao topo naquele ponto, ou null se o ponto estiver fora do viewport.

document.addEventListener('click', (event) => {
  const el = document.elementFromPoint(event.clientX, event.clientY);
  console.log('You clicked on:', el.tagName);
});

Uma armadilha comum: se você passar pageX/pageY em uma página rolada, ele retorna o elemento errado (ou null), porque espera coordenadas do viewport, e não do documento.

Manipulando posições de elementos com coordenadas

Você pode combinar essas peças para mover elementos. O método getBoundingClientRect() nos permite calcular o deslocamento entre o ponteiro e o canto do elemento, para que o elemento não "salte" até o cursor quando o arraste começa. Aqui está um quadrado que você pode arrastar:

Exemplo: elemento HTML arrastável

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Draggable Element Example</title>
    <style>
        #draggable {
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="draggable"></div>
    <script>
        const elem = document.getElementById('draggable');
        let shiftX, shiftY;

        function onMouseDown(event) {
            shiftX = event.clientX - elem.getBoundingClientRect().left;
            shiftY = event.clientY - elem.getBoundingClientRect().top;

            function moveAt(pageX, pageY) {
                elem.style.left = pageX - shiftX + 'px';
                elem.style.top = pageY - shiftY + 'px';
            }

            function onMouseMove(event) {
                moveAt(event.clientX + window.scrollX, event.clientY + window.scrollY);
            }

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', function stopDrag() {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', stopDrag);
                elem.removeEventListener('mousedown', onMouseDown);
            });
        }

        elem.addEventListener('mousedown', onMouseDown);
        elem.addEventListener('dragstart', function() { return false; });
    </script>
</body>
</html>
Nota

Para um melhor desempenho, considere usar transform: translate() em vez de left / top nas animações, pois isso evita recálculos de layout. Além disso, para compatibilidade com dispositivos móveis, adicione listeners de evento touchstart, touchmove e touchend junto com os eventos do mouse.

Animando com coordenadas

Você também pode controlar o movimento atualizando as coordenadas de um elemento a cada quadro com requestAnimationFrame. Multiplicar por um delta de tempo mantém a velocidade consistente, independentemente da taxa de atualização da tela:

Exemplo: objeto animado em movimento

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animation Using Coordinates</title>
</head>
<body>
    <div id="animateMe" style="width: 50px; height: 50px; background: blue; position: absolute;"></div>
    <button id="stopBtn">Stop Animation</button>
    <script>
        const target = document.getElementById('animateMe');
        const stopBtn = document.getElementById('stopBtn');
        let pos = 0;
        let isRunning = true;
        let lastTime = performance.now();

        function animate(currentTime) {
            if (!isRunning) return;
            const delta = (currentTime - lastTime) / 16; // Normalize to ~60fps
            lastTime = currentTime;
            if (pos >= 350) {
                pos = 0;
            }
            pos += delta;
            target.style.left = pos + 'px';
            requestAnimationFrame(animate);
        }

        stopBtn.addEventListener('click', () => { isRunning = false; });
        requestAnimationFrame(animate);
    </script>
</body>
</html>
Informação

Embora o JavaScript ofereça recursos poderosos para criar animações dinâmicas e interativas, as animações CSS costumam ser mais adequadas para animações mais simples. As animações CSS podem oferecer transições mais suaves e, em geral, são mais eficientes em desempenho, pois são tratadas diretamente pelo mecanismo de renderização do navegador, usando menos CPU. Isso as torna ideais para efeitos como transições, fades e movimentos básicos, especialmente quando alto desempenho e baixo uso de recursos são fundamentais.

Conclusão

O ponto principal é que o navegador tem dois referenciais de coordenadas: o viewport (clientX/Y, getBoundingClientRect(), elementFromPoint()) e o documento (pageX/Y), e você converte de um para o outro somando ou subtraindo window.scrollX/Y. Mantenha os dois bem separados e a maioria dos bugs de posicionamento desaparece.

A partir daqui, aprofunde-se em Tamanhos da janela e rolagem, em Rolagem para mover o viewport e em Fundamentos dos eventos do mouse para conhecer os eventos que entregam essas coordenadas.

Practice

Prática
O que as coordenadas do lado do cliente (client-side) em JavaScript representam?
O que as coordenadas do lado do cliente (client-side) em JavaScript representam?
Was this page helpful?