Поймать событие браузера «увеличить» в JavaScript


162

Можно ли с помощью JavaScript определить, когда пользователь изменяет масштаб страницы? Я просто хочу поймать событие «zoom» и ответить на него (аналогично событию window.onresize).

Спасибо.


13
Я считаю, что новые браузеры запускают событие onresize при увеличении страницы.
epascarello

1
У меня похожая проблема, но я не просто хочу знать, когда изменяется масштаб, я также хочу знать значение масштабирования при загрузке моей страницы.
Носредна

3
На самом деле не дубликат. Вопрос здесь в том, чтобы поймать событие, а не определить уровень масштабирования.
Ассаф Лави

5
@epascarello - да, но это не отменяет мой комментарий
HorusKol

7
Я хотел бы подтвердить, что «onresize» происходит в новейших браузерах. До сих пор я вижу, что newset Chrome (33), FF (28), IE (11 и 11 в 9-м режиме) правильно запускают событие при увеличении или уменьшении масштаба.
Брок

Ответы:


77

Там нет никакого способа, чтобы активно обнаружить, если есть зум. Я нашел хорошую запись здесь о том, как вы можете попытаться реализовать это.

Я нашел два способа определения уровня масштабирования. Один из способов обнаружения изменений уровня масштабирования основан на том факте, что процентные значения не увеличиваются. Процентное значение относится к ширине области просмотра и, следовательно, не зависит от масштаба страницы. Если вы вставите два элемента, один с позицией в процентах, а другой с одинаковой позицией в пикселях, они будут раздвигаться при увеличении страницы. Найдите соотношение между позициями обоих элементов, и вы получите уровень масштабирования. Смотрите тестовый пример. http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3

Вы также можете сделать это с помощью инструментов, описанных выше. Проблема в том, что вы более или менее делаете обоснованные предположения о том, увеличена ли страница. Это будет работать лучше в некоторых браузерах, чем в других.

Невозможно определить, увеличена ли страница, если они загружают вашу страницу во время увеличения.


1
Можно проверить параметры размера внутри обработчика загрузки для тела. Любопытно, что при отладке IE8 против 9 кажется, что обработчик onresize для тела передает документ в IE8, а IE9 пропускает окно, как и Chrome.
Уолтер К

Если вы поместите два элемента в одно и то же положение, а страница будет загружена при увеличении, не будет ли смещение между этими двумя элементами? Разве это не говорит о том, была ли страница уже увеличена или нет?
Виджаянт

такжеdocument.body.offsetWidth
Samy Bencherif

Использование такого метода также срабатывает, когда пользователь изменяет размер окна, так почему бы просто не использовать прослушиватель события resize? В этом случае это также жизнеспособное решение.
hewiefreeman

12

Я использую этот кусок JavaScript, чтобы реагировать на «события» Zoom.
Опрашивает ширину окна. (Как несколько предложено на этой странице (на которую ссылается Ян Эллиотт): http://novemberborn.net/javascript/page-zoom-ff3 [архив] )

Протестировано с Chrome, Firefox 3.6 и Opera, а не IE.

С уважением, Магнус

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

8
как насчет ложных срабатываний с изменением размеров окна?
2010 г.

5
Но вы можете отфильтровать те случаи, когда вы также реализуете обработчик onresize. Так что основы для решения здесь, я бы сказал.
Стейн де Витт

@StijndeWitt После прочтения этого я начинаю понимать предложенный путь, сейчас я работаю над решением с фильтрацией событий изменения размера, особенно на мобильных устройствах. Кто угодно?
lowtechsun

Может быть, вы могли бы использовать значение разрыва, чтобы различать изменение размера и масштабирование? Я имею в виду, что зум мгновенно изменит ширину окна на> х пикселей.
Себастьян

1
Ложные срабатывания являются проблемой, да. Разве это не устранит 99,99% всех ложных срабатываний, проверив, поддерживается ли соотношение сторон? Пусть скрипт запомнит последнее соотношение, вычислит новое и проверит, совпадают ли они. Объедините это с разной шириной, и у вас должна быть хорошая база
Биепбот фон Стерлинг

11

Хорошие новости всенекоторые люди! Новые браузеры будут вызывать событие изменения размера окна при изменении масштаба.


Chrome и Firefox для Android не будут вызывать событие изменения размера при увеличении.
lowtechsun

5
Это плохая новость, если вы не хотите, чтобы масштабирование вызывало событие изменения размера.
Реахрейк

12
Лучшей новостью будет фактическое событие масштабирования, отличное от события изменения размера.
Винсент

3
Оба правда. Ред.
Бен

можно ли как-то установить зум?
старик,

9

Давайте определим px_ratio как показано ниже:

отношение px = отношение физического пикселя к CSS;

если какой-либо один масштабирует страницу, область просмотра пикселей (px отличается от пикселя) уменьшается и должна соответствовать экрану, поэтому соотношение (физический пиксель / CSS_px) должно увеличиваться.

но в окне Изменение размера размер экрана уменьшается так же, как и пиксели. поэтому соотношение сохранится.

масштабирование: вызвать событие windows.resize -> и изменить px_ratio

но

изменение размера: вызвать событие windows.resize -> не изменить 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;
    }
}

Ключевым моментом является разница между CSS PX и Physical Pixel.

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


Это должен быть принятый ответ ИМО. Пока работает без нареканий, спасибо! Оберните это в пользовательское событие, если кому-то интересно gist.github.com/souporserious/b44ea5d04c38c2e7ff32cd1912a17cd0 .
Ужасно

6

Это работает для меня:

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

Это нужно только на IE8. Все остальные браузеры, естественно, генерируют событие изменения размера.


3

Есть встроенный плагин, yonranкоторый может сделать обнаружение. Вот его ранее отвеченный вопрос о StackOverflow. Это работает для большинства браузеров. Приложение так просто, как это:

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

демонстрация


Не работает на Firefox для Android 4.4.2. Также в FF для мобильного телефона отметьте Always enable zoomкнопку в опции Доступность. Демо не будет реагировать на изменения.
lowtechsun

2

Я хотел бы предложить улучшение предыдущего решения с отслеживанием изменений ширины окна. Вместо того чтобы хранить собственный массив прослушивателей событий, вы можете использовать существующую систему событий javascript, запускать собственное событие при изменении ширины и привязывать к нему обработчики событий.

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

function pollZoomFireEvent() 
{ 

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

Throttle / debounce может помочь в снижении скорости вызовов вашего обработчика.


1
Throttle / debounce полезна только в том случае, если опрос вызван некоторыми другими событиями (например, движение мыши или нажатие клавиши).
fuzzyTew

1

Вы также можете получить события изменения размера текста и коэффициент масштабирования, введя элемент div, содержащий как минимум неразрывный пробел (возможно, скрытый), и регулярно проверяя его высоту. Если высота изменяется, размер текста изменился (и вы знаете, сколько - это также срабатывает, кстати, если окно масштабируется в режиме полной страницы, и вы все равно получите правильный коэффициент масштабирования с той же высотой / соотношение высоты).



1

В iOS 10 можно добавить прослушиватель событий к touchmoveсобытию и определить, увеличена ли страница с текущим событием.

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


1

Хотя это 9-летний вопрос, проблема сохраняется!

Я обнаружил изменение размера, исключая масштабирование в проекте, поэтому я отредактировал свой код, чтобы он работал для определения как изменения размера, так и изменения масштаба друг от друга. Это работает большую часть времени, поэтому, если большинство достаточно хорошо для вашего проекта, то это должно быть полезно! Он обнаруживает увеличение в 100% случаев в том, что я тестировал до сих пор. Единственная проблема заключается в том, что если пользователь сходит с ума (то есть из-за скачкообразного изменения размера окна) или лаги окна, он может срабатывать как увеличение вместо изменения размера окна.

Он работает путем обнаружения изменения window.outerWidthили window.outerHeightизменения размера окна при обнаружении изменения window.innerWidthили window.innerHeightнезависимо от изменения размера окна в виде увеличения.

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

Примечание: мое решение похоже на решение KajMagnus, но оно мне помогло.


2
Я не буду голосовать, потому что вижу, что у вас есть решение, которое работает, и я понимаю, что делает ваш код, но я бы не рекомендовал использовать магическое число, такое как, 0.05по крайней мере, без хорошего объяснения. ;-) Например, я знаю, что в Chrome, в частности, 10% - это наименьшее значение, которое браузер будет увеличивать в любом случае, но не ясно, что это верно для других браузеров. К вашему сведению, всегда проверяйте ваш код и будьте готовы защитить или улучшить его.
Ноло

интересный подход
старик

1

Вот чистое решение:

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


это кажется довольно хорошим решением, и с Proxy вам даже не нужно было бы забивать свойство вдовы вашим перехватчиком - мы точно знаем, что обновление DPR - это то, что все браузеры делают / должны делать, когда пользователь увеличил масштаб? э-э ... вычеркните эту идею - окно не может быть проксировано, возможно, существует решение, использующее "matchMedia", но, поскольку это просто истина / ложь, я не уверен, как вы будете проверять изменения аналогового значения, такие как ДНР ...
Jon Z

0

Событие resize работает в современных браузерах, прикрепляя событие window, а затем считывая значения body, или другого элемента, например ( .getBoundingClientRect () ).

В некоторых более ранних браузерах было возможно зарегистрировать обработчики событий изменения размера для любого элемента HTML. Все еще возможно установить атрибуты onresize или использовать addEventListener (), чтобы установить обработчик для любого элемента. Однако события изменения размера генерируются только в объекте окна (т.е. возвращаются document.defaultView). Только обработчики, зарегистрированные в объекте окна, будут получать события изменения размера .

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

Другая альтернатива : API ResizeObserver

В зависимости от вашего макета, вы можете наблюдать за изменением размера на конкретном элементе.

Это хорошо работает на «адаптивных» макетах, потому что размер контейнера увеличивается при изменении масштаба.

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>


Будьте осторожны, чтобы не перегружать задачи javascript событиями жестов пользователя. Используйте requestAnimationFrame всякий раз, когда вам нужно перерисовать.


0

Согласно MDN, «matchMedia» является правильным способом сделать это https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes

это немного привередливо, потому что каждый экземпляр может смотреть только один MQ за раз, так что если вы заинтересованы в какое-либо изменение уровня масштабирования, вам нужно сделать несколько совпадений ... но так как браузер отвечает за выдачу событий, это, вероятно, все еще более производительный, чем опрос, и вы можете регулировать или дебатировать обратный вызов или прикреплять его к кадру анимации или чему-то еще - вот реализация, которая кажется довольно быстрой, не стесняйтесь менять местами _throttle или что-то еще, если вы уже зависите от этого.

Запустите фрагмент кода и увеличьте и уменьшите масштаб в вашем браузере, обратите внимание на обновленное значение в разметке - я проверял это только в Firefox! Дай мне знать, если увидишь какие-то проблемы.

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>


-4

Я отвечаю на 3-летнюю ссылку, но я думаю, что это более приемлемый ответ,

Создайте файл .css как,

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

НАПРИМЕР:-

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

Приведенный выше код делает размер шрифта «10 пикселей», когда экран увеличен примерно до 125%. Вы можете проверить другой уровень масштабирования, изменив значение «1000px».


Какая связь между max-widthи коэффициентом масштабирования?
Seebiscuit

Этот код будет выполняться на каждом экране <1000 пикселей и не имеет ничего общего с зумом
Artur Stary

@ArturStary нет способа определить, увеличен ли браузер, но есть много обходных путей, и один из них - это то, что я упомянул в своем ответе. Более того, я сказал, что вы можете изменить значение 1000px для проверки разных размеров экрана, что даст эффект разного уровня масштабирования
Saumil
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.