Ловіть подія "масштабування" браузера в JavaScript


162

Чи можливо виявити, використовуючи JavaScript, коли користувач змінює масштаб сторінки? Я просто хочу зловити подію "масштабування" та відповісти на неї (подібно до події window.onresize).

Дякую.


13
Я вважаю, що нові браузери запускають подію onresize, коли сторінку збільшують.
epascarello

1
У мене є аналогічна проблема, але я не просто хочу знати, коли змінюється масштабування, я хочу знати значення масштабу при завантаженні моєї сторінки.
Nosredna

3
Дійсно не дублікат. Питання тут полягає в тому, щоб зловити подію, а не визначити рівень масштабування.
Ассаф Лав'є

5
@epascarello - так, але це не
скасовує

7
Я хотів би підтвердити, що "нові зміни" виникають у новіших браузерах. Поки я бачу нові версії 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.
Вальтер К

Якщо ви розміщуєте два елементи в абсолютно однакових положеннях, а сторінка завантажується під час збільшення, чи не відбудеться зміщення між цими двома елементами? Хіба це не вказувало б, якщо сторінку вже збільшено чи ні?
vijayant

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

Використання такого способу також запуститься, коли користувач змінить розмір вікна, так чому б не просто використовувати слухач подій зміни розміру? У цьому випадку це також є життєздатним рішенням.
hewiefreeman

12

Я використовую цей фрагмент JavaScript, щоб реагувати на масштабування "подій".
Він опитує ширину вікна. (Як дещо пропонується на цій сторінці (до якої посилається Іан Еліотт): 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
як щодо помилкових позитивів з розмірами вікон?
gcb

5
Але ви можете відфільтрувати ті випадки, коли ви також впровадите обробник onresize. Тож, я б сказав, основи рішення.
Штійн де Вітт

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

Можливо, ви могли б використовувати значення пробілу для диференціації розміру та масштабування? Я маю на увазі, що масштаб миттєво змінить ширину вікна на> x пікселів.
Себастьєн

1
Хибні позитиви - це так. Чи не усуне це 99,99% усіх помилкових позитивних даних, перевіривши, чи підтримується співвідношення сторін? Нехай сценарій запам'ятає останнє співвідношення та обчислить нове співвідношення та перевірте, чи вони однакові. Поєднайте це, коли ширина буде різною, і ви повинні мати хорошу основу
Biepbot Von Stirling

11

Гарні новини всідеякі люди! Більш нові браузери запускають подію зміни розміру вікна при зміні масштабу.


Chrome і Firefox для Android не спровокують подію зміни розміру при збільшенні.
lowtechsun

5
Це не гарна новина, якщо ви не хочете, щоб збільшення зумовлювало подію зміни розміру.
Reahreic

12
Кращими новинами буде фактична подія масштабування, відмінна від події зміни розміру.
Вінсент

3
Обидва вірні. Відредаговано.
Бен

чи можна також якось встановити зум?
oldboy

9

Давайте визначимо px_ratio як нижче:

px співвідношення = відношення фізичного пікселя до css px.

якщо будь-який масштаб Сторінка, pxes вікна перегляду (px відрізняється від пікселя) зменшується і має бути придатним до екрана, тому співвідношення (фізичний піксель / CSS_px) повинно збільшуватися.

але у розмірі вікна розмір екрана зменшується, а також pxes. тому співвідношення збережеться.

масштабування: тригер 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 .
souporserious

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 for mobile позначте Always enable zoomкнопку в опції Accessibility. Демонстрація не реагує на зміну.
lowtechsun

2

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

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

function pollZoomFireEvent() 
{ 

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

Дросель / дебютація може допомогти зменшити кількість дзвінків вашого обробника.


1
Дросель / дебютація корисний лише в тому випадку, якщо опитування викликане деякими іншими подіями (наприклад, переміщенням миші або клавіатурою).
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% - це найменша сума, яку браузер збільшуватиме в будь-якому випадку, але не зрозуміло, що це стосується інших браузерів. fyi, завжди переконайтесь, що ваш код перевірений, і будьте готові захищати чи вдосконалювати його.
Ноло

цікавий підхід
oldboy

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


це здається досить приємним рішенням, і з проксі-сервісом вам навіть не потрібно було б пограбувати власністю вдови зі своїм перехоплювачем - чи ми точно знаємо, що оновлювати DPR - це те, що роблять / повинні робити всі браузери, коли користувач збільшує масштаб? е .. подряпайте цю ідею - вікно неможливо проксітизувати, можливо, рішення існує за допомогою "matchMedia", але оскільки це просто правда / хибна річ, я не впевнений, як би ви протестували зміни на аналогове значення, наприклад DPR ...
Jon z

0

Подія зміни розміру працює в сучасних браузерах, приєднуючи подію до 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

Я відповідаю на трирічне посилання, але, мабуть, ось більш прийнятна відповідь,

Створіть .css файл як,

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

EG: -

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

Вищевказаний код робить розмір шрифту '10px', коли екран збільшується приблизно до 125%. Ви можете перевірити різний рівень масштабування, змінивши значення "1000px".


Який взаємозв'язок між коефіцієнтом max-widthта коефіцієнтом збільшення?
seebiscuit

Цей код буде виконуватися на кожному <1000px екрані і не має нічого спільного із збільшенням
Artur Stary

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