Captura el evento de "zoom" del navegador en JavaScript

162

¿Es posible detectar, usando JavaScript, cuando el usuario cambia el zoom en una página? Simplemente quiero ver un evento de "zoom" y responder a él (similar al evento window.onresize).

Gracias.

usuario123093
fuente
13
Creo que los navegadores más nuevos activan el evento onresize cuando se amplía la página.
epascarello
1
Tengo un problema similar, pero no solo quiero saber cuándo se cambia el zoom, también quiero saber el valor del zoom cuando se carga mi página.
Nosredna
3
Realmente no es un duplicado. La pregunta aquí es sobre detectar el evento, no determinar el nivel de zoom.
Assaf Lavie
55
@epascarello - sí, pero no invalida mi comentario
HorusKol
77
Me gustaría confirmar que 'onresize' se produce en los navegadores más nuevos. Hasta ahora veo que el nuevo Chrome (33), FF (28), IE (11 y 11 en el noveno modo) activan correctamente el evento cuando acercas o alejas.
Brock

Respuestas:

77

No hay forma de detectar activamente si hay un zoom. Encontré una buena entrada aquí sobre cómo puede intentar implementarlo.

He encontrado dos formas de detectar el nivel de zoom. Una forma de detectar cambios en el nivel de zoom se basa en el hecho de que los valores porcentuales no se amplían. Un valor porcentual es relativo al ancho de la vista y, por lo tanto, no se ve afectado por el zoom de la página. Si inserta dos elementos, uno con una posición en porcentajes y otro con la misma posición en píxeles, se separarán cuando se amplíe la página. Encuentre la relación entre las posiciones de ambos elementos y obtendrá el nivel de zoom. Ver caso de prueba. http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3

También puedes hacerlo usando las herramientas de la publicación anterior. El problema es que estás más o menos haciendo conjeturas informadas sobre si la página se ha ampliado o no. Esto funcionará mejor en algunos navegadores que en otros.

No hay forma de saber si la página está ampliada si la cargan mientras está ampliada.

Ian Elliott
fuente
1
Uno puede verificar los parámetros de tamaño dentro del controlador de carga para el cuerpo. Curiosamente, al depurar IE8 vs. 9, parece que el controlador onresize para el cuerpo pasa el documento en IE8, mientras que IE9 pasa la ventana, al igual que Chrome.
Walter K
Si coloca los dos elementos en exactamente las mismas posiciones y la página se carga mientras está ampliada, ¿no habría un desplazamiento entre los dos elementos? ¿No diría esto si la página ya estaba ampliada o no?
vijayant
tambiéndocument.body.offsetWidth
Samy Bencherif
El uso de un método como este también se activará cuando un usuario cambie el tamaño de la ventana, entonces, ¿por qué no usar el detector de eventos de cambio de tamaño? En cuyo caso también es una solución viable.
Hewiefreeman
12

Estoy usando este fragmento de JavaScript para reaccionar a los "eventos" de Zoom.
Sondea el ancho de la ventana. (Como se sugiere en esta página (que Ian Elliott enlazó): http://novemberborn.net/javascript/page-zoom-ff3 [archivo] )

Probado con Chrome, Firefox 3.6 y Opera, no IE.

Saludos, 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
fuente
8
¿Qué pasa con los falsos positivos con el cambio de tamaño de la ventana?
gcb
55
Pero puede filtrar esos casos cuando también implementa un controlador onresize. Entonces, diría que lo básico para una solución está aquí.
Stijn de Witt
@StijndeWitt Después de leer esto , estoy empezando a comprender la ruta propuesta, ahora estoy trabajando en una solución para filtrar eventos de cambio de tamaño, especialmente en dispositivos móviles. ¿Nadie?
lowtechsun
¿Quizás podría usar un valor de espacio para diferenciar el tamaño y el zoom? Quiero decir que el zoom cambiará instantáneamente el ancho de la ventana en> x píxeles.
Sebastien
1
Los falsos positivos son un problema, sí. ¿No eliminaría el 99.99% de todos los falsos positivos al verificar si se mantuvo la relación de aspecto? Haga que el guión recuerde la última relación y calcule la nueva relación y verifique si son iguales. Combina eso con un ancho diferente y deberías tener una buena base
Biepbot Von Stirling
11

Buenas noticias todo el mundo¡algunas personas! Los navegadores más nuevos activarán un evento de cambio de tamaño de ventana cuando se cambie el zoom.

Ben
fuente
Chrome y Firefox para Android no activarán el evento de cambio de tamaño en el zoom.
lowtechsun
55
Esta no es una buena noticia si no desea que el zoom active el evento de cambio de tamaño.
Reahreic 01 de
12
Una mejor noticia sería un evento de zoom real, distinto del evento de cambio de tamaño.
Vincent
3
Ambas verdad. Editado
Ben
¿es posible configurar también el zoom de alguna manera?
oldboy
9

Vamos a definir px_ratio de la siguiente manera:

px px = ratio de píxeles físicos a css px.

si alguno amplía la página, los pxes de la ventana gráfica (px es diferente del píxel) se reducen y deben ajustarse a la pantalla, por lo que la relación (píxel físico / CSS_px) debe aumentar.

pero en el cambio de tamaño de la ventana, se reduce el tamaño de la pantalla y los px entonces la relación se mantendrá.

zoom: activa el evento windows.resize -> y cambia px_ratio

pero

cambio de tamaño: desencadenar evento windows.resize -> no cambia 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;
    }
}

El punto clave es la diferencia entre CSS PX y Physical Pixel.

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

Abilogos
fuente
Esta debería ser la respuesta aceptada de la OMI. Funciona perfectamente hasta ahora, ¡gracias! Envuelto en un evento personalizado si alguien está interesado en gist.github.com/souporserious/b44ea5d04c38c2e7ff32cd1912a17cd0 .
souporserious
6

Esto funciona para mi:

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

Solo es necesario en IE8. Todos los otros navegadores generan naturalmente un evento de cambio de tamaño.

Bill Keese
fuente
3

Hay un ingenioso complemento creado a partir de yonraneso que puede hacer la detección. Aquí está su pregunta previamente respondida sobre StackOverflow. Funciona para la mayoría de los navegadores. La aplicación es tan simple 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
        : "");
}

Manifestación

Starx
fuente
No funciona en Firefox para Android 4.4.2. También en FF para dispositivos móviles, marque el Always enable zoombotón en la opción Accesibilidad. La demostración no reaccionará al cambio.
lowtechsun
2

Me gustaría sugerir una mejora a la solución anterior con el seguimiento de los cambios en el ancho de la ventana. En lugar de mantener su propia variedad de oyentes de eventos, puede usar el sistema de eventos javascript existente y activar su propio evento al cambiar el ancho, y vincular los controladores de eventos.

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

function pollZoomFireEvent() 
{ 

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

Acelerador / rebote puede ayudar a reducir la tasa de llamadas de su controlador.

Alexey
fuente
1
Throttle / debounce es útil solo si otros eventos activan el sondeo (por ejemplo, movimiento del mouse o keyup).
fuzzyTew
1

También puede obtener los eventos de cambio de tamaño del texto y el factor de zoom inyectando un div que contenga al menos un espacio no rompible (posiblemente, oculto) y verificando regularmente su altura. Si la altura cambia, el tamaño del texto ha cambiado (y usted sabe cuánto, esto también se dispara, por cierto, si la ventana se amplía en modo de página completa, y aún obtendrá el factor de zoom correcto, con la misma altura / relación de altura).

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

En iOS 10 es posible agregar un detector de touchmoveeventos al evento y detectar, si la página está ampliada con el evento actual.

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

alwe
fuente
1

Aunque esta es una pregunta de 9 años, ¡el problema persiste!

He estado detectando el cambio de tamaño al excluir el zoom en un proyecto, por lo que edité mi código para que funcione para detectar tanto el cambio de tamaño como el zoom exclusivos entre sí. Funciona la mayor parte del tiempo, por lo que si la mayoría es lo suficientemente bueno para su proyecto, ¡esto debería ser útil! Detecta el zoom el 100% del tiempo en lo que he probado hasta ahora. El único problema es que si el usuario se vuelve loco (es decir, si cambia el tamaño de la ventana de forma espasmódica) o si la ventana se retrasa, puede activarse como un zoom en lugar de un cambio de tamaño de la ventana.

Funciona mediante la detección de un cambio en el tamaño de la ventana window.outerWidtho window.outerHeightal cambiar el tamaño de la ventana, mientras que detecta un cambio en el tamaño de la ventana window.innerWidtho window.innerHeightindependiente del mismo como un 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: Mi solución es como la de KajMagnus, pero me ha funcionado mejor.

dandeto
fuente
2
No votaré en contra porque puedo ver que tienes una solución que funciona, y entiendo lo que hace tu código, pero no recomendaría usar un número mágico como, 0.05por lo menos, sin dar una buena explicación. ;-) Por ejemplo, sé que en Chrome, específicamente, el 10% es la cantidad más pequeña que el navegador ampliará en cualquier caso, pero no está claro que esto sea cierto para otros navegadores. Para tu información, asegúrate siempre de que tu código esté probado y prepárate para defenderlo o mejorarlo.
Nolo
enfoque interesante
oldboy
1

Aquí hay una solución limpia:

// 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
fuente
Esta parece ser una solución bastante buena, y con un Proxy ni siquiera necesitaría bloquear la propiedad de la viuda con su interceptor. ¿Sabemos con certeza que la actualización del DPR es lo que todos los navegadores hacen / deberían hacer cuando el usuario ha ampliado? er ... descarte esa idea: la ventana no puede ser representada, tal vez exista una solución usando "matchMedia", pero dado que es solo algo verdadero / falso, no estoy seguro de cómo probaría los cambios en un valor analógico como el DPR ...
Jon z
0

El evento de cambio de tamaño funciona en los navegadores modernos al adjuntar el evento windowy luego leer los valores del bodyu otro elemento con, por ejemplo ( .getBoundingClientRect () ).

En algunos navegadores anteriores, era posible registrar controladores de eventos de cambio de tamaño en cualquier elemento HTML. Todavía es posible establecer atributos onresize o usar addEventListener () para establecer un controlador en cualquier elemento. Sin embargo, los eventos de cambio de tamaño solo se activan en el objeto de la ventana (es decir, devuelto por document.defaultView). Solo los controladores registrados en el objeto de la ventana recibirán eventos de cambio de tamaño .

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

Otra alternativa : la API ResizeObserver

Dependiendo de su diseño, puede mirar para cambiar el tamaño de un elemento en particular.

Esto funciona bien en diseños "receptivos", porque la caja del contenedor cambia de tamaño al hacer 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>


Tenga cuidado de no sobrecargar las tareas de JavaScript de los eventos de gestos del usuario. Use requestAnimationFrame cuando necesite redibujar.

NVRM
fuente
0

Según MDN, "matchMedia" es la forma correcta de hacer esto https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes

es un poco complicado porque cada instancia solo puede ver un MQ a la vez, por lo que si está interesado en cualquier cambio en el nivel de zoom, debe hacer un montón de coincidencias ... pero dado que el navegador se encarga de emitir los eventos, probablemente sea aún más eficiente que el sondeo, y podría acelerar o cancelar la devolución de llamada o fijarla a un cuadro de animación o algo así; aquí hay una implementación que parece bastante ágil, no dude en intercambiar _ acelerador o lo que sea si ya depende de eso.

Ejecute el fragmento de código y amplíe y reduzca en su navegador, observe el valor actualizado en el marcado: ¡solo lo probé en Firefox! déjame saber si ves algún 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
fuente
-4

Estoy respondiendo a un enlace de 3 años pero creo que aquí hay una respuesta más aceptable,

Crear archivo .css como

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

P.EJ:-

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

El código anterior hace que el tamaño de la fuente '10px' cuando la pantalla se amplía a aproximadamente 125%. Puede verificar diferentes niveles de zoom cambiando el valor de '1000px'.

Saumil
fuente
¿Cuál es la relación entre max-widthy la relación de zoom?
seebiscuit
Este código se ejecutará en cada pantalla <1000px y no tiene nada que ver con el zoom
Artur Stary
@ArturStary no hay forma de detectar si el navegador está ampliado, pero hay muchas soluciones y una de ellas es lo que he mencionado en mi respuesta. Además, dije que puede cambiar el valor de 1000px para verificar diferentes tamaños de pantalla que darán el efecto de diferentes niveles de zoom
Saumil