Capture o evento "zoom" do navegador em JavaScript

162

É possível detectar, usando JavaScript, quando o usuário altera o zoom em uma página? Eu simplesmente quero pegar um evento "zoom" e responder a ele (semelhante ao evento window.onresize).

Obrigado.

user123093
fonte
13
Acredito que os navegadores mais recentes disparam o evento onresize quando a página é ampliada.
22410 epascarello
1
Tenho um problema semelhante, mas não quero apenas saber quando o zoom é alterado, também quero saber o valor do zoom quando minha página é carregada.
Nosredna
3
Realmente não é uma duplicata. A questão aqui é sobre a captura do evento, não a determinação do nível de zoom.
Assaf Lavie
5
@epascarello - sim, mas isso não invalida o meu comentário
HorusKol 20/12/12
7
Gostaria de confirmar que o 'onresize' ocorre nos navegadores mais recentes. Até agora, vejo o jornal Chrome (33), FF (28), IE (11 e 11 no 9º modo), todos disparam corretamente o evento quando você aumenta ou diminui o zoom.
Brock

Respostas:

77

Não há como detectar ativamente se há um zoom. Encontrei uma boa entrada aqui sobre como você pode tentar implementá-lo.

Eu encontrei duas maneiras de detectar o nível de zoom. Uma maneira de detectar alterações no nível de zoom depende do fato de que os valores percentuais não são ampliados. Um valor percentual é relativo à largura da janela de exibição e, portanto, não é afetado pelo zoom da página. Se você inserir dois elementos, um com posição em porcentagem e outro com a mesma posição em pixels, eles se afastarão quando a página for ampliada. Encontre a proporção entre as posições dos dois elementos e você terá o nível de zoom. Veja o caso de teste. http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3

Você também pode fazer isso usando as ferramentas da postagem acima. O problema é que você está mais ou menos tentando adivinhar se a página foi ou não ampliada. Isso funcionará melhor em alguns navegadores do que em outros.

Não há como saber se a página está com zoom, se eles carregam sua página enquanto estiver com zoom.

Ian Elliott
fonte
1
Pode-se verificar os parâmetros de tamanho dentro do manipulador de onload para o corpo. Curiosamente, na depuração do IE8 vs. 9, parece que o manipulador de redimensionamento para o corpo recebe o documento no IE8, enquanto o IE9 passa pela janela, assim como o Chrome.
Walter K
Se você colocar os dois elementos exatamente nas mesmas posições e a página carregar enquanto estiver ampliada, não haveria um deslocamento entre os dois elementos? Isso não indica se a página já foi ampliada ou não?
vijayant
tambémdocument.body.offsetWidth
Samy Bencherif
O uso de um método como esse também será acionado quando um usuário redimensiona a janela. Por que não usar apenas o ouvinte de eventos de redimensionamento? Nesse caso, também é uma solução viável.
precisa saber é o seguinte
12

Estou usando este pedaço de JavaScript para reagir aos "eventos" do Zoom.
Ele examina a largura da janela. (Como sugerido nesta página (ao qual Ian Elliott se vinculou): http://novemberborn.net/javascript/page-zoom-ff3 [arquivo] )

Testado com Chrome, Firefox 3.6 e Opera, não com o IE.

Atenciosamente, Magnus

var zoomListeners = [];

(function(){
  // Poll the pixel width of the window; invoke zoom listeners
  // if the width has been changed.
  var lastWidth = 0;
  function pollZoomFireEvent() {
    var widthNow = jQuery(window).width();
    if (lastWidth == widthNow) return;
    lastWidth = widthNow;
    // Length changed, user must have zoomed, invoke listeners.
    for (i = zoomListeners.length - 1; i >= 0; --i) {
      zoomListeners[i]();
    }
  }
  setInterval(pollZoomFireEvent, 100);
})();
KajMagnus
fonte
8
e os falsos positivos com redimensionamentos de janelas?
gcb
5
Mas você pode filtrar esses casos ao implementar um manipulador de redimensionamento. Então, o básico para uma solução está aqui, eu diria.
Stijn de Witt
@StijndeWitt Depois de ler isso , estou começando a entender o caminho proposto, agora estou trabalhando em uma solução para filtrar eventos de redimensionamento, especialmente em dispositivos móveis. Qualquer um?
lowtechsun
Talvez você possa usar um valor de intervalo para diferenciar o redimensionamento e o zoom? Quero dizer, o zoom mudará instantaneamente a largura da janela em> x pixels.
Sebastien
1
Falsos positivos são um problema, sim. Não eliminaria 99,99% de todos os falsos positivos, verificando se a proporção foi mantida? Faça com que o script lembre da última proporção e calcule a nova proporção e verifique se são iguais. Combine isso com a largura sendo diferente e você deverá ter uma boa base
Biepbot Von Stirling
11

Boas notícias todosalgumas pessoas! Navegadores mais recentes acionarão um evento de redimensionamento de janela quando o zoom for alterado.

Ben
fonte
Chrome e Firefox para Android não acionam o evento de redimensionamento no zoom.
precisa saber é o seguinte
5
Isso não é uma boa notícia se você não deseja que o zoom acione o evento de redimensionamento.
Reahreic
12
Notícias melhores seriam um evento de zoom real, diferente do evento de redimensionamento.
22417 Vincent Vincent
3
Ambos verdadeiros. Editado.
Ben
é possível também definir o zoom de alguma forma?
oldboy 18/05/19
9

Vamos definir px_ratio como abaixo:

relação do px = relação do pixel físico ao css px.

se houver um zoom na página, os pixels da janela de visualização (o px é diferente do pixel) são reduzidos e devem ser ajustados à tela para que a proporção (pixel físico / CSS_px) seja maior.

mas no redimensionamento da janela, o tamanho da tela também reduz os pixels. então a proporção será mantida.

zoom: aciona o evento windows.resize -> e altera px_ratio

mas

redimensionar: acionar evento windows.resize -> não altera px_ratio

//for zoom detection
px_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;

$(window).resize(function(){isZooming();});

function isZooming(){
    var newPx_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
    if(newPx_ratio != px_ratio){
        px_ratio = newPx_ratio;
        console.log("zooming");
        return true;
    }else{
        console.log("just resizing");
        return false;
    }
}

O ponto principal é a diferença entre CSS PX e Physical Pixel.

https://gist.github.com/abilogos/66aba96bb0fb27ab3ed4a13245817d1e

Abrange
fonte
Esta deve ser a resposta aceita IMO. Funciona perfeitamente até agora, obrigado! Agrupe-o em um evento personalizado se alguém estiver interessado gist.github.com/souporserious/b44ea5d04c38c2e7ff32cd1912a17cd0 .
souporserious 15/10/19
6

Isso funciona para mim:

        var deviceXDPI = screen.deviceXDPI;
        setInterval(function(){
            if(screen.deviceXDPI != deviceXDPI){
                deviceXDPI = screen.deviceXDPI;
                ... there was a resize ...
            }
        }, 500);

Só é necessário no IE8. Todos os outros navegadores geram naturalmente um evento de redimensionamento.

Bill Keese
fonte
3

Existe um plugin bacana construído a partir dele yonranque pode fazer a detecção. Aqui está sua pergunta respondida anteriormente no StackOverflow. Funciona para a maioria dos navegadores. A aplicação é tão simples como esta:

window.onresize = function onresize() {
  var r = DetectZoom.ratios();
  zoomLevel.innerHTML =
    "Zoom level: " + r.zoom +
    (r.zoom !== r.devicePxPerCssPx
        ? "; device to CSS pixel ratio: " + r.devicePxPerCssPx
        : "");
}

Demo

Starx
fonte
Não funciona no Firefox para Android 4.4.2. Também no FF para dispositivos móveis, marque o Always enable zoombotão na opção Acessibilidade. A demonstração não reagirá à mudança.
precisa saber é o seguinte
2

Gostaria de sugerir uma melhoria para a solução anterior com o rastreamento de alterações na largura da janela. Em vez de manter sua própria matriz de ouvintes de eventos, você pode usar o sistema de eventos javascript existente e acionar seu próprio evento após a alteração da largura e vincular manipuladores de eventos a ele.

$(window).bind('myZoomEvent', function() { ... });

function pollZoomFireEvent() 
{ 

    if ( ... width changed ... ) {
        $(window).trigger('myZoomEvent');
    }
}

A aceleração / rejeição pode ajudar a reduzir a taxa de chamadas de seu manipulador.

Alexey
fonte
1
A aceleração / rejeição é útil apenas se a pesquisa for acionada por outros eventos (por exemplo, movimento do mouse ou keyup).
fuzzyTew
1

Você também pode obter os eventos de redimensionamento de texto e o fator de zoom injetando uma div contendo pelo menos um espaço que não pode ser quebrado (possivelmente oculto) e verificando regularmente sua altura. Se a altura mudar, o tamanho do texto foi alterado (e você sabe quanto - isso também dispara, aliás, se a janela for ampliada no modo de página inteira e você ainda obterá o fator de zoom correto, com a mesma altura / relação altura).

Argo
fonte
1
<script>
var zoomv = function() {
  if(topRightqs.style.width=='200px){
  alert ("zoom");
  }
};
zoomv();
</script>
SchoolforDesign
fonte
1

No iOS 10, é possível adicionar um ouvinte de touchmoveevento ao evento e detectar se a página é ampliada com o evento atual.

var prevZoomFactorX;
var prevZoomFactorY;
element.addEventListener("touchmove", (ev) => {
  let zoomFactorX = document.documentElement.clientWidth / window.innerWidth;
  let zoomFactorY = document.documentElement.clientHeight / window.innerHeight;
  let pageHasZoom = !(zoomFactorX === 1 && zoomFactorY === 1);

  if(pageHasZoom) {
    // page is zoomed
    
    if(zoomFactorX !== prevZoomFactorX || zoomFactorY !== prevZoomFactorY) {
      // page is zoomed with this event
    }
  }
  prevZoomFactorX = zoomFactorX;
  prevZoomFactorY = zoomFactorY;
});

ame
fonte
1

Embora essa seja uma pergunta antiga de 9 anos, o problema persiste!

Eu tenho detectado o redimensionamento e excluído o zoom em um projeto. Por isso, editei meu código para fazê-lo funcionar para detectar o redimensionamento e o zoom exclusivos um do outro. Funciona na maioria das vezes, portanto, se a maioria é boa o suficiente para o seu projeto, isso deve ser útil! Ele detecta o zoom 100% do tempo no que testei até agora. O único problema é que, se o usuário ficar louco (ou seja, redimensionar espasticamente a janela) ou se a janela ficar lenta, poderá disparar como um zoom em vez de redimensionar a janela.

Ele funciona detectando uma alteração no redimensionamento de uma janela window.outerWidthou window.outerHeightao mesmo tempo em que detecta uma alteração window.innerWidthou window.innerHeightum redimensionamento independente da janela como um zoom.

//init object to store window properties
var windowSize = {
  w: window.outerWidth,
  h: window.outerHeight,
  iw: window.innerWidth,
  ih: window.innerHeight
};

window.addEventListener("resize", function() {
  //if window resizes
  if (window.outerWidth !== windowSize.w || window.outerHeight !== windowSize.h) {
    windowSize.w = window.outerWidth; // update object with current window properties
    windowSize.h = window.outerHeight;
    windowSize.iw = window.innerWidth;
    windowSize.ih = window.innerHeight;
    console.log("you're resizing"); //output
  }
  //if the window doesn't resize but the content inside does by + or - 5%
  else if (window.innerWidth + window.innerWidth * .05 < windowSize.iw ||
    window.innerWidth - window.innerWidth * .05 > windowSize.iw) {
    console.log("you're zooming")
    windowSize.iw = window.innerWidth;
  }
}, false);

Nota: Minha solução é como a do KajMagnus, mas isso funcionou melhor para mim.

dandeto
fonte
2
Não vou votar porque posso ver que você tem uma solução que funciona e entendo o que seu código faz, mas não recomendo o uso de um número mágico, como por exemplo, 0.05sem pelo menos fornecer uma boa explicação. ;-) Por exemplo, eu sei que no Chrome, especificamente, 10% é a menor quantidade que o navegador ampliará em qualquer caso, mas não está claro se isso é verdade para outros navegadores. fyi, sempre verifique se seu código foi testado e esteja preparado para defendê-lo ou melhorá-lo.
Nolo
abordagem interessante
oldboy 18/05/19
1

Aqui está uma solução limpa:

// polyfill window.devicePixelRatio for IE
if(!window.devicePixelRatio){
  Object.defineProperty(window,'devicePixelRatio',{
    enumerable: true,
    configurable: true,
    get:function(){
      return screen.deviceXDPI/screen.logicalXDPI;
    }
  });
}
var oldValue=window.devicePixelRatio;
window.addEventListener('resize',function(e){
  var newValue=window.devicePixelRatio;
  if(newValue!==oldValue){
    // TODO polyfill CustomEvent for IE
    var event=new CustomEvent('devicepixelratiochange');
    event.oldValue=oldValue;
    event.newValue=newValue;
    oldValue=newValue;
    window.dispatchEvent(event);
  }
});

window.addEventListener('devicepixelratiochange',function(e){
  console.log('devicePixelRatio changed from '+e.oldValue+' to '+e.newValue);
});

fuweichin
fonte
esta parece ser uma solução bastante agradável e, com um Proxy, você nem precisa usar a propriedade widow com seu interceptador - sabemos com certeza que a atualização do DPR é o que todos os navegadores fazem / devem fazer quando o usuário aumenta o zoom? er .. risque essa idéia - a janela não pode ser copiada por proxy, talvez exista uma solução usando "matchMedia", mas como é apenas uma coisa verdadeira / falsa, não sei como você testaria alterações em um valor analógico como o DPR ...
Jon z
0

O evento redimensionar funciona em navegadores modernos, anexando o evento windowe lendo os valores do bodyou de outro elemento com, por exemplo ( .getBoundingClientRect () ).

Em alguns navegadores anteriores, era possível registrar redimensionadores de manipuladores de eventos em qualquer elemento HTML. Ainda é possível definir atributos de redimensionamento ou usar addEventListener () para definir um manipulador em qualquer elemento. No entanto, os eventos de redimensionamento são acionados apenas no objeto da janela (ou seja, retornados por document.defaultView). Somente manipuladores registrados no objeto de janela receberão eventos de redimensionamento .

window.addEventListener("resize", getSizes, false)
        
function getSizes(){
  let body = document.body
  console.log(body.clientWidth +"px x "+ body.clientHeight + "px")
}

Outra alternativa : a API ResizeObserver

Dependendo do layout, você pode assistir ao redimensionamento de um elemento específico.

Isso funciona bem em layouts «responsivos», porque a caixa do contêiner é redimensionada ao aplicar zoom.

function watchBoxchange(e){
 // console.log(e[0].contentBoxSize.inlineSize+" "+e[0].contentBoxSize.blockSize)
 info.textContent = e[0].contentBoxSize.inlineSize+" * "+e[0].contentBoxSize.blockSize + "px"
}

new ResizeObserver(watchBoxchange).observe(fluid)
#fluid {
  width: 200px;
  height:100px;
  overflow: auto;
  resize: both;
  border: 3px black solid;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 8vh
}
<div id="fluid">
   <info id="info"></info> 
</div>


Cuidado para não sobrecarregar tarefas javascript dos eventos de gestos do usuário. Use requestAnimationFrame sempre que precisar redesenhar.

NVRM
fonte
0

De acordo com a MDN, "matchMedia" é a maneira correta de fazer isso https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes

é um pouco complicado, porque cada instância pode assistir apenas um MQ de cada vez, por isso, se você estiver interessado em qualquer alteração no nível de zoom, precisará fazer vários matchers .. mas como o navegador é responsável por emitir os eventos, provavelmente ainda mais eficiente que a pesquisa, e você pode limitar ou rejeitar o retorno de chamada ou fixá-lo em um quadro de animação ou algo assim - aqui está uma implementação que parece bastante rápida, sinta-se à vontade para trocar de acelerador ou qualquer outra coisa, se você já depende disso.

Execute o trecho de código e aumente e diminua o zoom no seu navegador, observe o valor atualizado na marcação - só testei isso no Firefox! deixe-me saber se você vê algum problema.

const el = document.querySelector('#dppx')

if ('matchMedia' in window) {
  function observeZoom(cb, opts) {
    opts = {
      // first pass for defaults - range and granularity to capture all the zoom levels in desktop firefox
      ceiling: 3,
      floor: 0.3,
      granularity: 0.05,
      ...opts
    }
    const precision = `${opts.granularity}`.split('.')[1].length

    let val = opts.floor
    const vals = []
    while (val <= opts.ceiling) {
      vals.push(val)
      val = parseFloat((val + opts.granularity).toFixed(precision))
    }

    // construct a number of mediamatchers and assign CB to all of them
    const mqls = vals.map(v => matchMedia(`(min-resolution: ${v}dppx)`))

    // poor person's throttle
    const throttle = 3
    let last = performance.now()
    mqls.forEach(mql => mql.addListener(function() {
      console.debug(this, arguments)
      const now = performance.now()
      if (now - last > throttle) {
        cb()
        last = now
      }
    }))
  }

  observeZoom(function() {
    el.innerText = window.devicePixelRatio
  })
} else {
  el.innerText = 'unable to observe zoom level changes, matchMedia is not supported'
}
<div id='dppx'>--</div>

Jon z
fonte
-4

Estou respondendo a um link de 3 anos, mas acho que aqui está uma resposta mais aceitável,

Crie o arquivo .css como,

@media screen and (max-width: 1000px) 
{
       // things you want to trigger when the screen is zoomed
}

POR EXEMPLO:-

@media screen and (max-width: 1000px) 
{
    .classname
    {
          font-size:10px;
    }
}

O código acima torna o tamanho da fonte '10px' quando a tela é ampliada para aproximadamente 125%. Você pode verificar diferentes níveis de zoom alterando o valor de '1000px'.

Saumil
fonte
Qual é a relação entre a max-widthe a taxa de zoom?
seebiscuit
Este código será executado em cada <1000px tela e não tem nada a ver com zoom
Artur Stary
@ArturStary não há como detectar se o navegador está com zoom, mas existem muitas soluções alternativas e uma delas é o que mencionei na minha resposta. Além disso, eu disse que você pode alterar o valor de 1000px para verificar se há diferentes tamanhos de tela que lhe dará o efeito do nível de zoom diferente
Saumil