Catch browser's “zoom” event in JavaScript

前端 未结 16 1560
眼角桃花
眼角桃花 2020-11-22 05:45

Is it possible to detect, using JavaScript, when the user changes the zoom in a page? I simply want to catch a \"zoom\" event and respond to it (similar to window.onresize e

相关标签:
16条回答
  • 2020-11-22 06:07

    According to MDN, "matchMedia" is the proper way to do this https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes

    it's a bit finicky because each instance can only watch one MQ at a time, so if you're interested in any zoom level change you need to make a bunch of matchers.. but since the browser is in charge to emitting the events it's probably still more performant than polling, and you could throttle or debounce the callback or pin it to an animation frame or something - here's an implementation that seems pretty snappy, feel free to swap in _throttle or whatever if you're already depending on that.

    Run the code snippet and zoom in and out in your browser, note the updated value in the markup - I only tested this in Firefox! lemme know if you see any issues.

    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>

    0 讨论(0)
  • 2020-11-22 06:08

    There is a nifty plugin built from yonran that can do the detection. Here is his previously answered question on StackOverflow. It works for most of the browsers. Application is as simple as this:

    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

    0 讨论(0)
  • 2020-11-22 06:12

    Here is a clean solution:

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

    0 讨论(0)
  • 2020-11-22 06:16

    I'd like to suggest an improvement to previous solution with tracking changes to window width. Instead of keeping your own array of event listeners you can use existing javascript event system and trigger your own event upon width change, and bind event handlers to it.

    $(window).bind('myZoomEvent', function() { ... });
    
    function pollZoomFireEvent() 
    { 
    
        if ( ... width changed ... ) {
            $(window).trigger('myZoomEvent');
        }
    }
    

    Throttle/debounce can help with reducing the rate of calls of your handler.

    0 讨论(0)
提交回复
热议问题