Capturez l'événement "zoom" du navigateur en JavaScript

162

Est-il possible de détecter, à l'aide de JavaScript, lorsque l'utilisateur modifie le zoom d'une page? Je veux simplement attraper un événement "zoom" et y répondre (similaire à l'événement window.onresize).

Merci.

user123093
la source
13
Je crois que les nouveaux navigateurs déclenchent l'événement onresize lorsque la page est agrandie.
epascarello
1
J'ai un problème similaire, mais je ne veux pas seulement savoir quand le zoom est changé, je veux aussi connaître la valeur du zoom lors du chargement de ma page.
Nosredna
3
Vraiment pas un double. La question ici est de capturer l'événement, pas de déterminer le niveau de zoom.
Assaf Lavie
5
@epascarello - oui, mais cela n'invalide pas mon commentaire
HorusKol
7
Je voudrais confirmer que «onresize» apparaît dans les navigateurs les plus récents. Jusqu'à présent, je vois que les newset Chrome (33), FF (28), IE (11 et 11 en 9e mode) déclenchent tous correctement l'événement lorsque vous effectuez un zoom avant ou arrière.
Brock

Réponses:

77

Il n'y a aucun moyen de détecter activement s'il y a un zoom. J'ai trouvé une bonne entrée ici sur la façon dont vous pouvez essayer de l'implémenter.

J'ai trouvé deux façons de détecter le niveau de zoom. Une façon de détecter les changements de niveau de zoom repose sur le fait que les valeurs de pourcentage ne sont pas agrandies. Une valeur de pourcentage est relative à la largeur de la fenêtre et n'est donc pas affectée par le zoom de la page. Si vous insérez deux éléments, l'un avec une position en pourcentages et l'autre avec la même position en pixels, ils s'écarteront lorsque la page sera agrandie. Trouvez le rapport entre les positions des deux éléments et vous avez le niveau de zoom. Voir le cas de test. http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3

Vous pouvez également le faire en utilisant les outils de l'article ci-dessus. Le problème est que vous faites plus ou moins des suppositions éclairées pour savoir si la page a zoomé ou non. Cela fonctionnera mieux dans certains navigateurs que dans d'autres.

Il n'y a aucun moyen de savoir si la page est agrandie s'ils chargent votre page tout en zoomant.

Ian Elliott
la source
1
On peut vérifier les paramètres de taille dans le gestionnaire onload pour le corps. Curieusement, lors du débogage d'IE8 contre 9, il semble que le gestionnaire onresize du corps passe le document dans IE8, tandis que IE9 passe la fenêtre, tout comme Chrome.
Walter K
Si vous placez les deux éléments exactement aux mêmes positions et que la page se charge pendant le zoom, n'y aurait-il pas un déplacement entre les deux éléments? Cela ne dirait-il pas si la page a déjà été agrandie ou non?
vijayant
aussidocument.body.offsetWidth
Samy Bencherif
L'utilisation d'une méthode comme celle-ci se déclenchera également lorsqu'un utilisateur redimensionne la fenêtre, alors pourquoi ne pas simplement utiliser l'écouteur d'événements de redimensionnement? Dans ce cas, c'est aussi une solution viable.
hewiefreeman
12

J'utilise ce morceau de JavaScript pour réagir aux "événements" de Zoom.
Il interroge la largeur de la fenêtre. (Comme suggéré un peu sur cette page (à laquelle Ian Elliott a lié): http://novemberborn.net/javascript/page-zoom-ff3 [archive] )

Testé avec Chrome, Firefox 3.6 et Opera, pas IE.

Cordialement, 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
la source
8
qu'en est-il des faux positifs avec le redimensionnement de la fenêtre?
gcb
5
Mais vous pouvez filtrer ces cas lorsque vous implémentez également un gestionnaire onresize. Je dirais donc que les bases d'une solution sont ici.
Stijn de Witt
@StijndeWitt Après avoir lu ceci, je commence à comprendre le chemin proposé, maintenant je travaille sur une solution avec filtrage des événements de redimensionnement, en particulier sur mobile. N'importe qui?
lowtechsun
Peut-être pourriez-vous utiliser une valeur d'espace pour différencier le redimensionnement et le zoom? Je veux dire que le zoom changera instantanément la largeur de la fenêtre de> x pixels.
Sébastien
1
Les faux positifs sont un problème, oui. N'éliminerait-il pas 99,99% de tous les faux positifs en vérifiant si le rapport hauteur / largeur était conservé? Demandez au script de se souvenir du dernier rapport et de calculer le nouveau rapport et de vérifier si ce sont les mêmes. Combinez cela avec une largeur différente et vous devriez avoir une bonne base
Biepbot Von Stirling
11

Bonnes nouvelles toutes les personnesquelques personnes! Les navigateurs plus récents déclenchent un événement de redimensionnement de la fenêtre lorsque le zoom est modifié.

Ben
la source
Chrome et Firefox pour Android ne déclenchent pas l'événement de redimensionnement lors du zoom.
lowtechsun
5
Ce n'est pas une bonne nouvelle si vous ne souhaitez pas que le zoom déclenche l'événement de redimensionnement.
Reahreic
12
Une meilleure nouvelle serait un événement de zoom réel, distinct de l'événement de redimensionnement.
Vincent
3
Les deux sont vrais. Édité.
Ben
est-il possible de régler également le zoom d'une manière ou d'une autre?
oldboy
9

Définissons px_ratio comme ci-dessous:

ratio px = ratio du pixel physique au px css.

s'il y a un zoom sur la page, les pixels de la fenêtre (px est différent du pixel) se réduisent et doivent être adaptés à l'écran de sorte que le rapport (pixel physique / CSS_px) doit devenir plus grand.

mais dans le redimensionnement de la fenêtre, la taille de l'écran diminue ainsi que les pixels. donc le ratio se maintiendra.

zoom: déclencher l'événement windows.resize -> et modifier px_ratio

mais

redimensionnement: déclenche l'événement windows.resize -> ne change pas 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;
    }
}

Le point clé est la différence entre CSS PX et Physical Pixel.

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

Abilogos
la source
Cela devrait être la réponse acceptée de l'OMI. Fonctionne parfaitement jusqu'à présent, merci! Enveloppé dans un événement personnalisé si quelqu'un est intéressé gist.github.com/souporserious/b44ea5d04c38c2e7ff32cd1912a17cd0 .
souporserious
6

Cela fonctionne pour moi:

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

Il n'est nécessaire que sur IE8. Tous les autres navigateurs génèrent naturellement un événement de redimensionnement.

Bill Keese
la source
3

Il existe un plugin astucieux yonranqui peut faire la détection. Voici son déjà répondu à la question sur StackOverflow. Cela fonctionne pour la plupart des navigateurs. L'application est aussi simple que ceci:

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

Démo

Starx
la source
Ne fonctionne pas sur Firefox pour Android 4.4.2. Toujours dans FF pour mobile, cochez le Always enable zoombouton dans l'option Accessibilité. La démo ne réagira pas au changement.
lowtechsun
2

Je voudrais suggérer une amélioration de la solution précédente avec le suivi des modifications de la largeur de la fenêtre. Au lieu de conserver votre propre tableau d'écouteurs d'événements, vous pouvez utiliser le système d'événements javascript existant et déclencher votre propre événement lors d'un changement de largeur, et y lier des gestionnaires d'événements.

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

function pollZoomFireEvent() 
{ 

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

Throttle / debounce peut aider à réduire le taux d'appels de votre gestionnaire.

Alexey
la source
1
Throttle / debounce n'est utile que si l'interrogation est déclenchée par d'autres événements (par exemple, le mouvement de la souris ou le keyup).
fuzzyTew
1

Vous pouvez également obtenir les événements de redimensionnement du texte et le facteur de zoom en injectant un div contenant au moins un espace insécable (éventuellement masqué) et en vérifiant régulièrement sa hauteur. Si la hauteur change, la taille du texte a changé (et vous savez à quel point - cela se déclenche également, d'ailleurs, si la fenêtre est agrandie en mode pleine page, et vous obtiendrez toujours le facteur de zoom correct, avec la même hauteur / rapport de hauteur).

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

Sur iOS 10, il est possible d'ajouter un écouteur d'événement à l' touchmoveévénement et de détecter si la page est agrandie avec l'événement en cours.

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
la source
1

Bien qu'il s'agisse d'une question vieille de 9 ans, le problème persiste!

J'ai détecté le redimensionnement tout en excluant le zoom dans un projet, j'ai donc modifié mon code pour le faire fonctionner pour détecter à la fois le redimensionnement et le zoom exclusifs l'un de l'autre. Cela fonctionne la plupart du temps, donc si la plupart sont assez bons pour votre projet, cela devrait être utile! Il détecte le zoom à 100% du temps dans ce que j'ai testé jusqu'à présent. Le seul problème est que si l'utilisateur devient fou (c'est-à-dire redimensionner spastiquement la fenêtre) ou si la fenêtre est en retard, il peut se déclencher comme un zoom au lieu d'un redimensionnement de la fenêtre.

Il fonctionne en détectant un changement dans window.outerWidthou window.outerHeightcomme redimensionnement de la fenêtre tout en détectant un changement dans window.innerWidthou window.innerHeightindépendamment du redimensionnement de la fenêtre comme 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);

Remarque: Ma solution est comme celle de KajMagnus, mais cela a mieux fonctionné pour moi.

dandeto
la source
2
Je ne voterai pas parce que je peux voir que vous avez une solution qui fonctionne, et je comprends ce que fait votre code, mais je ne recommanderais pas d'utiliser un nombre magique tel que 0.05sans au moins fournir une bonne explication. ;-) Par exemple, je sais que dans Chrome, en particulier, 10% est la plus petite quantité de zoom du navigateur dans tous les cas, mais il n'est pas clair que cela soit vrai pour les autres navigateurs. Pour info, assurez-vous toujours que votre code est testé et soyez prêt à le défendre ou à l'améliorer.
Nolo
approche intéressante
oldboy
1

Voici une solution propre:

// 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
la source
cela semble être une solution plutôt intéressante, et avec un proxy, vous n'auriez même pas besoin de couper la propriété de la veuve avec votre intercepteur - savons-nous avec certitude que la mise à jour du DPR est ce que tous les navigateurs font / devraient faire lorsque l'utilisateur a zoomé? euh .. effacez cette idée - la fenêtre ne peut pas être mandatée, peut-être qu'une solution existe en utilisant "matchMedia" mais comme c'est juste une chose vraie / fausse, je ne sais pas comment vous testeriez les modifications d'une valeur analogique comme le DPR ...
Jon z
0

L'événement de redimensionnement fonctionne sur les navigateurs modernes en attachant l'événement à window, puis en lisant les valeurs de body, ou d'un autre élément avec par exemple ( .getBoundingClientRect () ).

Dans certains navigateurs précédents, il était possible d'enregistrer des gestionnaires d'événements de redimensionnement sur n'importe quel élément HTML. Il est toujours possible de définir des attributs onresize ou d'utiliser addEventListener () pour définir un gestionnaire sur n'importe quel élément. Cependant, les événements de redimensionnement ne sont déclenchés que sur l'objet window (c'est-à-dire retournés par document.defaultView). Seuls les gestionnaires enregistrés sur l'objet window recevront des événements de redimensionnement .

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

Une autre alternative : l'API ResizeObserver

En fonction de votre mise en page, vous pouvez surveiller le redimensionnement d'un élément particulier.

Cela fonctionne bien sur les mises en page «réactives», car la boîte du conteneur est redimensionnée lors du 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>


Veillez à ne pas surcharger les tâches javascript des événements de gestes de l'utilisateur. Utilisez requestAnimationFrame chaque fois que vous avez besoin de rafraîchissements .

NVRM
la source
0

Selon MDN, "matchMedia" est le bon moyen de le faire https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes

c'est un peu capricieux car chaque instance ne peut regarder qu'un seul MQ à la fois, donc si vous êtes intéressé par un changement de niveau de zoom, vous devez faire un tas de matchers ... mais comme le navigateur est chargé d'émettre les événements, c'est probablement encore plus performant que l'interrogation, et vous pouvez ralentir ou annuler le rappel ou l'épingler à une image d'animation ou quelque chose comme ça - voici une implémentation qui semble assez rapide, n'hésitez pas à échanger _throttle ou autre chose si vous en dépendez déjà.

Exécutez l'extrait de code et effectuez un zoom avant et arrière dans votre navigateur, notez la valeur mise à jour dans le balisage - je n'ai testé cela que dans Firefox! laissez-moi savoir si vous voyez des problèmes.

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
la source
-4

Je réponds à un lien vieux de 3 ans mais je suppose que voici une réponse plus acceptable,

Créez un fichier .css en tant que,

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

PAR EXEMPLE:-

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

Le code ci-dessus rend la taille de la police «10px» lorsque l'écran est agrandi à environ 125%. Vous pouvez vérifier les différents niveaux de zoom en modifiant la valeur de «1000px».

Saumil
la source
Quelle est la relation entre le max-widthet le taux de zoom?
seebiscuit
Ce code sera exécuté sur chaque écran <1000px et n'a rien à voir avec le zoom
Artur Stary
@ArturStary il n'y a aucun moyen de détecter si le navigateur est zoomé mais il existe de nombreuses solutions de contournement et l'une d'entre elles est ce que j'ai mentionné dans ma réponse. De plus, j'ai dit que vous pouvez changer la valeur de 1000px pour vérifier différentes tailles d'écran qui donneront l'effet d'un niveau de zoom différent
Saumil