Bagaimana cara mendeteksi dimensi DIV berubah?

351

Saya sudah html sampel berikut, ada DIV yang memiliki lebar 100%. Ini mengandung beberapa elemen. Saat melakukan pengubahan ukuran jendela, elemen-elemen dalam dapat diposisikan ulang, dan dimensi div dapat berubah. Saya bertanya apakah mungkin untuk mengaitkan peristiwa perubahan dimensi div? dan Bagaimana cara melakukannya? Saat ini saya mengikat fungsi panggilan balik ke acara mengubah ukuran jQuery pada DIV target, namun, tidak ada log konsol yang dihasilkan, lihat di bawah:

Sebelum Mengubah Ukuran masukkan deskripsi gambar di sini

<html>
<head>
    <script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
    <script type="text/javascript" language="javascript">
            $('#test_div').bind('resize', function(){
                console.log('resized');
            });
    </script>
</head>
<body>
    <div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" />
        <input type="button" value="button 2" />
        <input type="button" value="button 3" />
    </div>
</body>
</html>
William Choi
sumber
7
Ini tidak akan berhasil karena, Anda mengikat acara pengubahan ukuran ke elemen div yang ditentukan. Tetapi mengubah ukuran acara akan memicu untuk jendela bukan untuk elemen Anda.
Fatih Acet
Anda bisa menggunakan di setIntervalsini sebagai solusi yang memungkinkan. Anda selalu dapat mengikat clearIntervaltombol klik untuk menghentikan loop setelah pekerjaan Anda selesai.
Peter Darmis
1
Sejak 2020, ResizeObserver bekerja di semua browser utama (Chrome, Safari, Firefox, Edge) kecuali IE. caniuse.com/#feat=resizeobserver
Dan

Jawaban:

264

Ada metode yang sangat efisien untuk menentukan apakah ukuran elemen telah diubah.

http://marcj.github.io/css-element-queries/

Perpustakaan ini memiliki kelas ResizeSensoryang dapat digunakan untuk mengubah ukuran deteksi.
Ini menggunakan pendekatan berbasis peristiwa, jadi sangat cepat dan tidak membuang waktu CPU.

Contoh:

new ResizeSensor(jQuery('#divId'), function(){ 
    console.log('content dimension changed');
});

Tolong jangan gunakan plugin jQuery onresize seperti yang digunakan setTimeout()dalam kombinasi dengan membaca DOM clientHeight/ clientWidthproperti dalam satu lingkaran untuk memeriksa perubahan.
Ini luar biasa lambat dan tidak akurat karena menyebabkan tata letak meronta-ronta .

Pengungkapan: Saya terkait langsung dengan perpustakaan ini.

Marc J. Schmidt
sumber
8
@jake, dukungan IE11 sekarang diterapkan.
Marc J. Schmidt
15
@Aletheios Anda harus melihat lebih dekat. requestAnimationFrame bukan untuk deteksi dimensi lambat tetapi justru sebaliknya: Ini menghaluskan semua kejadian ekstrem yang dipicu oleh sensor pengubah ukuran berdasarkan peristiwa nyata untuk menghemat waktu cpu. Lihat dokumen terbaru di github.com/marcj/css-element-queries/blob/master/src/…
Marc J. Schmidt
6
@Aletheios Saya kira Anda melewatkan satu poin: setTimeout tidak hanya tidak akurat tetapi lambat sekali. RequestAnimationFrame yang memeriksa boolean sederhana adalah urutan besarnya lebih cepat daripada setTimeout yang memeriksa setiap saat terhadap properti DOM. Selain itu setTimeout juga disebut setiap beberapa milidetik.
Marc J. Schmidt
9
@Orwellophile Anda jelas tidak mengerti apa-apa. Hentikan downvoting jawaban di mana Anda tidak tahu cara kerjanya secara internal. Lagi-lagi: css-element-query: menggunakan ukuran peristiwa nyata yang dipicu setiap kali dimensi berubah. Karena Anda akan mendapatkan terlalu banyak acara (untuk drag'n'drop misalnya) itu menambahkan requestAnimationFrame memeriksa variabel boolean sederhana untuk memuluskan peristiwa yang dipicu tersebut ke 1 / bingkai. jquery plugin: Menggunakan setTimeout memeriksa semua waktu alat peraga DOM (clientHeight dll) di mana setiap pemeriksaan sangat lambat tetapi perlu sering diperiksa untuk memiliki fungsi yang sama.
Marc J. Schmidt
9
@Aletheios, @Orwellophile, dan siapa pun yang mungkin telah menyesatkan mereka: requestAnimationFramevs setTimeouttidak relevan, perpustakaan ini tidak berulang . requestAnimationFramehanya digunakan untuk debounce / throttle frekuensi menelepon callback Anda, itu tidak polling . MutationObserver secara teoritis dapat digunakan untuk efek yang sama tetapi tidak di browser lama, tidak seperti ini. Ini luar biasa pintar.
Han Seoul-Oh
251

Standar baru untuk ini adalah api Ubah Ukuran Pengamat, tersedia di Chrome 64.

function outputsize() {
 width.value = textbox.offsetWidth
 height.value = textbox.offsetHeight
}
outputsize()

new ResizeObserver(outputsize).observe(textbox)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>

Ubah Ukuran Pengamat

Spesifikasi: https://wicg.github.io/ResizeObserver

Polyfill: https://github.com/WICG/ResizeObserver/issues/3

Masalah Firefox: https://bugzil.la/1272409

Masalah Safari: http://wkb.ug/157743

Dukungan Saat Ini: http://caniuse.com/#feat=resizeobserver

Daniel Herr
sumber
3
belum berguna untuk situs produksi, karena hanya Chrome yang mendukungnya saat ini. Tapi yang pasti solusi terbaik di sini! btw, berikut ini gambaran umum singkat dari status implementasi: chromestatus.com/feature/5705346022637568
Philipp
8
@ Pilip Ada polyfill.
Daniel Herr
2
Tampaknya ini masih fitur eksperimental di semua browser caniuse.com/#feat=resizeobserver
Francesc Rosas
5
Bahkan 2 tahun setelah jawaban ini, dukungan browser suram: caniuse.com/#search=resizeobserver
Scrimothy
2
Bekerja seperti pesona di Chrome dan FF terbaru.
Klemen Tušar
32

Anda harus mengikat resizeacara pada windowobjek, bukan pada elemen html generik.

Anda kemudian dapat menggunakan ini:

$(window).resize(function() {
    ...
});

dan dalam fungsi panggilan balik Anda dapat memeriksa lebar divpanggilan Anda yang baru

$('.a-selector').width();

Jadi, jawaban untuk pertanyaan Anda adalah tidak , Anda tidak dapat mengikat resizeacara ke div.

Alessandro Vendruscolo
sumber
124
jawaban ini tidak cukup baik. perubahan ukuran mungkin terjadi tanpa mengubah ukuran jendela APAPUN. sama sekali.
vsync
9
misalnya ketika Anda memiliki elemen splitter atau hanya menambahkan beberapa teks atau konten lain ke suatu elemen.
Günter Zöchbauer
@Anu tidak benar, Anda dapat memiliki misalnya modifikasi DOM melalui javascript menyebabkan CSS untuk diterapkan pada struktur baru yang menghasilkan tata letak yang berbeda - ukuran setiap wadah.
Antoniossss
29

Jangka panjang, Anda akan dapat menggunakan ResizeObserver .

new ResizeObserver(callback).observe(element);

Sayangnya saat ini tidak didukung secara default di banyak browser.

Sementara itu, Anda dapat menggunakan fungsi seperti berikut ini. Karena, sebagian besar perubahan ukuran elemen akan berasal dari mengubah ukuran jendela atau mengubah sesuatu di DOM. Anda dapat mendengarkan pengubahan ukuran jendela dengan peristiwa pengubahan ukuran jendela dan Anda dapat mendengarkan perubahan DOM menggunakan MutationObserver .

Berikut adalah contoh fungsi yang akan memanggil Anda kembali ketika ukuran elemen yang disediakan berubah sebagai akibat dari salah satu dari peristiwa tersebut:

var onResize = function(element, callback) {
  if (!onResize.watchedElementData) {
    // First time we are called, create a list of watched elements
    // and hook up the event listeners.
    onResize.watchedElementData = [];

    var checkForChanges = function() {
      onResize.watchedElementData.forEach(function(data) {
        if (data.element.offsetWidth !== data.offsetWidth ||
            data.element.offsetHeight !== data.offsetHeight) {
          data.offsetWidth = data.element.offsetWidth;
          data.offsetHeight = data.element.offsetHeight;
          data.callback();
        }
      });
    };

    // Listen to the window's size changes
    window.addEventListener('resize', checkForChanges);

    // Listen to changes on the elements in the page that affect layout 
    var observer = new MutationObserver(checkForChanges);
    observer.observe(document.body, { 
      attributes: true,
      childList: true,
      characterData: true,
      subtree: true 
    });
  }

  // Save the element we are watching
  onResize.watchedElementData.push({
    element: element,
    offsetWidth: element.offsetWidth,
    offsetHeight: element.offsetHeight,
    callback: callback
  });
};
nkron
sumber
1
Sayangnya, tampaknya MutationObserver tidak dapat mendeteksi perubahan di Shadow DOM.
Ajedi32
@nkron, Sayangnya, jika Anda menggunakan ukuran CSS3 , mengubah ukuran dapat terjadi ketika pengguna mengubah ukuran secara manual. Acara ini tidak dapat ditangkap.
Pacerier
@ Ajedi32 Ya, tapi Anda bisa menggunakan MutationObserver di dalam ShadowDOM. Maksud saya, alih-alih menonton bodyseperti dalam contoh ini, Anda dapat menonton elemen secara langsung. Itu sebenarnya lebih performan dan efisien daripada menonton seluruh tubuh.
trusktr
@ Ajedi32 Nah, satu-satunya masalah adalah, jika pohon Shadow DOM ditutup, maka Anda tidak akan dapat mengakses internal, tapi saya tidak berpikir itu biasanya sesuatu yang perlu Anda lakukan, dan jika Anda perlu mengamati pohon tertutup seperti itu maka mungkin ada masalah dalam desain. Idealnya, Anda hanya perlu mengamati elemen Anda sendiri yang memiliki akses. Komponen Shadow-DOM yang mengharuskan Anda untuk mengamati ukuran internal itu mungkin tidak dirancang dengan benar. Apakah Anda memiliki contoh spesifik? Jika demikian, mungkin saya bisa membantu menyarankan solusi (karena saya tertarik juga, tapi belum punya contoh).
trusktr
@trusktr Saya tidak memiliki MCVE saat ini, tetapi pada dasarnya setiap elemen Shadow DOM yang dapat mengubah ukurannya sebagai hasil dari beberapa input pengguna akan cukup untuk memicu masalah ini. Kotak yang diperluas dengan beberapa informasi tambahan saat diklik, misalnya. Perhatikan bahwa saya mengamati halaman, bukan elemen individual, karena saya ingin menggulir ke bawah secara otomatis ketika ukuran halaman meningkat, apa pun alasannya. Elemen Shadow DOM yang meluas hanyalah salah satu dari banyak alasan yang mungkin mengapa halaman mungkin menjadi lebih lama.
Ajedi32
24

ResizeSensor.js adalah bagian dari perpustakaan besar, tapi saya mengurangi fungsinya menjadi INI :

function ResizeSensor(element, callback)
{
    let zIndex = parseInt(getComputedStyle(element));
    if(isNaN(zIndex)) { zIndex = 0; };
    zIndex--;

    let expand = document.createElement('div');
    expand.style.position = "absolute";
    expand.style.left = "0px";
    expand.style.top = "0px";
    expand.style.right = "0px";
    expand.style.bottom = "0px";
    expand.style.overflow = "hidden";
    expand.style.zIndex = zIndex;
    expand.style.visibility = "hidden";

    let expandChild = document.createElement('div');
    expandChild.style.position = "absolute";
    expandChild.style.left = "0px";
    expandChild.style.top = "0px";
    expandChild.style.width = "10000000px";
    expandChild.style.height = "10000000px";
    expand.appendChild(expandChild);

    let shrink = document.createElement('div');
    shrink.style.position = "absolute";
    shrink.style.left = "0px";
    shrink.style.top = "0px";
    shrink.style.right = "0px";
    shrink.style.bottom = "0px";
    shrink.style.overflow = "hidden";
    shrink.style.zIndex = zIndex;
    shrink.style.visibility = "hidden";

    let shrinkChild = document.createElement('div');
    shrinkChild.style.position = "absolute";
    shrinkChild.style.left = "0px";
    shrinkChild.style.top = "0px";
    shrinkChild.style.width = "200%";
    shrinkChild.style.height = "200%";
    shrink.appendChild(shrinkChild);

    element.appendChild(expand);
    element.appendChild(shrink);

    function setScroll()
    {
        expand.scrollLeft = 10000000;
        expand.scrollTop = 10000000;

        shrink.scrollLeft = 10000000;
        shrink.scrollTop = 10000000;
    };
    setScroll();

    let size = element.getBoundingClientRect();

    let currentWidth = size.width;
    let currentHeight = size.height;

    let onScroll = function()
    {
        let size = element.getBoundingClientRect();

        let newWidth = size.width;
        let newHeight = size.height;

        if(newWidth != currentWidth || newHeight != currentHeight)
        {
            currentWidth = newWidth;
            currentHeight = newHeight;

            callback();
        }

        setScroll();
    };

    expand.addEventListener('scroll', onScroll);
    shrink.addEventListener('scroll', onScroll);
};

Bagaimana cara menggunakannya:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
Martin Wantke
sumber
Inilah yang grafik lakukan.
Antoniossss
Terima kasih telah mengompresi itu. Saya akan mencobanya :)
Tony K.
Ada sesuatu yang hilang dalam ungkapan iniparseInt( getComputedStyle(element) )
GetFree
2
Saya bingung bagaimana "biarkan zIndex = parseInt (getComputedStyle (elemen));" sah? Saya berasumsi Anda bermaksud: "biarkan zIndex = parseInt (getComputedStyle (elemen) .zIndex);
Ryan Badour
1
Solusi / kode di atas tidak mendeteksi semua peristiwa pengubahan ukuran (mis. Jika sebuah kontainer diubah ukurannya dengan alat itu tidak mendeteksi pengubahan ukuran untuk acara anak-anak. Lihat jsfiddle.net/8md2rfsy/1 ). Jawaban yang diterima berfungsi (batalkan komentar pada baris masing-masing).
newBee
21

Saya TIDAK merekomendasikan setTimeout()hack karena memperlambat kinerja! Sebagai gantinya, Anda dapat menggunakan pengamat mutasi DOM untuk mendengarkan perubahan ukuran Div.

JS:

var observer = new MutationObserver(function(mutations) {
    console.log('size changed!');
  });
  var target = document.querySelector('.mydiv');
  observer.observe(target, {
    attributes: true
  });

HTML:

<div class='mydiv'>
</div>

Ini biola
Cobalah untuk mengubah ukuran div.


Anda selanjutnya dapat membungkus metode Anda dalam debouncemetode untuk meningkatkan efisiensi. debounceakan memicu metode Anda setiap x milidetik alih-alih memicu setiap milidetik ukuran sedang DIV.

JerryGoyal
sumber
9
Satu-satunya masalah dengan yang satu ini adalah tidak melaporkan perubahan kecuali mutasi benar-benar terjadi pada elemen. Jika elemen diubah ukurannya karena elemen lain sedang ditambahkan, ini tidak akan berfungsi.
Tony K.
Saya setuju dengan @TonyK., Ini adalah satu-satunya masalah saya dengan MutationObserver dan sekarang saya sedang mencari pustaka ukuran elemen
Murhaf Sousli
@JerryGoyal Tidak berfungsi saat div berisi tag <img>. Pada contoh pertama gambar tidak dimuat dan ukuran div ditetapkan sebagai 0 dan setelah memuat ukuran div diubah, tetapi pengamat tidak dipicu
Santhosh
15

Solusi terbaik adalah dengan menggunakan Elemen Query . Namun, mereka tidak standar, tidak ada spesifikasi - dan satu-satunya pilihan adalah menggunakan salah satu dari polyfill / libraries yang tersedia, jika Anda ingin pergi dengan cara ini.

Gagasan di balik kueri elemen adalah untuk memungkinkan wadah tertentu pada halaman untuk menanggapi ruang yang disediakan untuk itu. Ini akan memungkinkan untuk menulis komponen sekali dan kemudian menjatuhkannya di mana saja pada halaman, sementara itu akan menyesuaikan isinya dengan ukuran saat ini. Tidak peduli apa ukuran Jendela itu. Ini adalah perbedaan pertama yang kita lihat antara kueri elemen dan kueri media. Semua orang berharap bahwa pada suatu titik suatu spesifikasi akan dibuat yang akan membakukan permintaan elemen (atau sesuatu yang mencapai tujuan yang sama) dan menjadikannya asli, bersih, sederhana, dan kuat. Kebanyakan orang setuju bahwa pertanyaan Media sangat terbatas dan tidak membantu untuk desain modular dan responsif sebenarnya.

Ada beberapa polyfill / perpustakaan yang memecahkan masalah dengan cara yang berbeda (bisa disebut workarounds alih-alih solusi):

Saya telah melihat solusi lain untuk masalah serupa yang diajukan. Biasanya mereka menggunakan timer atau ukuran Window / viewport di bawah tenda, yang bukan solusi nyata. Lebih jauh, saya pikir idealnya ini harus diselesaikan terutama dalam CSS, dan bukan dalam javascript atau html.

Stan
sumber
+1 untuk kueri elemen, tetapi saya tidak berharap melihatnya dalam waktu dekat. Bahkan tidak ada draf spesifikasi pada titik ini AFAIK ... mungkin salah satu dari kami pihak yang tertarik harus menyatukan dan mengirimkannya?
Rob Evans
1
Itu ide yang bagus tentu saja. Akan membutuhkan waktu yang lama untuk mencapai versi final, banyak perhatian perlu ditarik. Merancang ini dengan cara yang baik sepertinya tidak sepele bagi saya. Kompatibilitas lintas-browser, dependensi melingkar, debugging, keterbacaan dan pemeliharaan dll. Jadi mari kita mulai sedini mungkin :)
Stan
@Stan, saya pikir jawaban ini bisa lebih baik dengan menambahkan lebih banyak elaborasi tentang apa itu Elemen Query.
Pacerier
15

Saya menemukan perpustakaan ini berfungsi ketika solusi MarcJ tidak:

https://github.com/sdecima/javascript-detect-element-resize

Ini sangat ringan dan bahkan mendeteksi ukuran alami melalui CSS atau hanya memuat / rendering HTML.

Contoh kode (diambil dari tautan):

<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
  var resizeElement = document.getElementById('resizeElement'),
      resizeCallback = function() {
          /* do something */
      };
  addResizeListener(resizeElement, resizeCallback);
  removeResizeListener(resizeElement, resizeCallback);
</script>
Joshcomley
sumber
Ini dia. 147 baris, gunakan acara gulir. Baca kode ini jika Anda menginginkan jawaban yang lengkap dan benar tentang cara terbaik mendeteksi perubahan dimensi div. Petunjuk: ini bukan "cukup gunakan perpustakaan ini."
Seph Reed
1
Orang-orang harus melihat masalah ini sebelum mereka mencoba perpustakaan ini.
Louis
13

Lihatlah http://benalman.com/code/projects/jquery-resize/examples/resize/ ini.

Ini memiliki berbagai contoh. Coba ubah ukuran jendela Anda dan lihat bagaimana elemen di dalam elemen wadah disesuaikan.

Contoh dengan js fiddle untuk menjelaskan cara membuatnya bekerja.
Lihatlah biola ini http://jsfiddle.net/sgsqJ/4/

Dalam peristiwa resize () terikat pada elemen yang memiliki kelas "test" dan juga ke objek window dan dalam mengubah ukuran callback objek window $ ('. Test'). Resize () dipanggil.

misalnya

$('#test_div').bind('resize', function(){
            console.log('resized');
});

$(window).resize(function(){
   $('#test_div').resize();
});
Bharat Patil
sumber
Menarik. Jsfiddle itu secara konsisten crash chrome dengan inspektur terbuka dan saya mengklik tautan "Tambahkan lebih banyak teks". Firefox melempar kesalahan "terlalu banyak rekursi".
EricP
3
Tolong jangan gunakan plugin resize jQuery. Ini sangat lambat dan tidak akurat juga. Lihat jawaban saya untuk mendapatkan solusi yang lebih baik.
Marc J. Schmidt
24
Ini TIDAK langsung mendeteksi perubahan dimensi. itu hanya produk sampingan dari pendengar acara ukuran. a perubahan dimensi dapat terjadi dalam banyak cara lain.
vsync
4
Bagaimana jika ukuran div adalah dari perubahan gaya atau dom dan bukan oleh ukuran jendela?
awe
9

Anda dapat menggunakan iframeatau objectmenggunakan contentWindowatau contentDocumentmengubah ukuran. Tanpa setIntervalatausetTimeout

Langkah langkah:

  1. Atur posisi elemen Anda ke relative
  2. Tambahkan di dalam IFRAME tersembunyi mutlak transparan
  3. Dengarkan IFRAME.contentWindow- onresizeacara

Contoh HTML:

<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>

Javascript:

$('div').width(100).height(100);
$('div').animate({width:200},2000);

$('object').attr({
    type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
    console.log('ooooooooonload')
})

$($('iframe')[0].contentWindow).on('resize',function(){
    console.log('div changed')
})

Contoh Menjalankan

JsFiddle: https://jsfiddle.net/qq8p470d/


Lihat lebih lanjut:

Aminadav Glickshtein
sumber
1
Catatan untuk mereka yang tidak menggunakan jquery, IFRAME.contentWindowtidak tersedia sampai onloadacara tersebut diaktifkan pada iframe. Juga, Anda mungkin ingin menambahkan gaya visibility:hidden;, karena iframe dapat memblokir input mouse ke elemen di belakangnya (tampaknya "mengambang" di IE setidaknya).
James Wilkins
3
Meskipun ini bekerja, ini adalah solusi berat dengan overhead menciptakan konteks penelusuran yang sama sekali baru hanya untuk mengamati perubahan ukuran. Juga, dalam beberapa kasus tidak ideal atau layak untuk mengubah displayproperti elemen.
trusktr
setuju, ini adalah solusi mengerikan seperti yang disebutkan di atas
29er
Bagaimana aja solusi yang begitu mengerikan?
Aminadav Glickshtein
metode ini bekerja dengan baik dengan pemutar video dan pemutar audio yang membutuhkan pengubahan ukuran konten dinamis. Saya menggunakan metode ini untuk mengubah ukuran output Waveurfer.js.
David Clews
8

Hanya objek window yang menghasilkan event "resize". Satu-satunya cara saya tahu untuk melakukan apa yang ingin Anda lakukan adalah menjalankan penghitung waktu interval yang secara berkala memeriksa ukurannya.

Runcing
sumber
7

var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));

function checkResize (mutations) {
    var el = mutations[0].target;
    var w = el.clientWidth;
    var h = el.clientHeight;
    
    var isChange = mutations
      .map((m) => m.oldValue + '')
      .some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);

    if (!isChange)
      return;

    var event = new CustomEvent('resize', {detail: {width: w, height: h}});
    el.dispatchEvent(event);
}

var observer = new MutationObserver(checkResize); 
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
<div id = "div">DIV</div>

Aikon Mogwai
sumber
7

Hebatnya setua masalah ini, ini masih menjadi masalah di sebagian besar browser.

Seperti yang dikatakan orang lain, Chrome 64+ sekarang disertakan dengan Resize Observes secara asli, namun, spesifikasi tersebut masih diselaraskan dan Chrome sekarang sedang (pada 2019-01-29) di belakang edisi terbaru dari spesifikasi .

Saya telah melihat beberapa polyfill ResizeObserver yang baik di alam bebas, namun, beberapa tidak mengikuti spesifikasi yang erat dan yang lain memiliki beberapa masalah perhitungan.

Saya sangat membutuhkan perilaku ini untuk membuat beberapa komponen web responsif yang dapat digunakan dalam aplikasi apa pun. Untuk membuatnya bekerja dengan baik, mereka perlu mengetahui dimensi mereka setiap saat, jadi ResizeObservers terdengar ideal dan saya memutuskan untuk membuat polyfill yang mengikuti spesifikasi sedekat mungkin.

Repo: https://github.com/juggle/resize-observer

Demo: https://codesandbox.io/s/myqzvpmmy9

Trem
sumber
Ini jelas merupakan jawaban yang pasti bagi siapa pun yang berurusan dengan kompatibilitas browser IE11 +.
ekfuhrmann
3

Menggunakan Clay.js ( https://github.com/zzarcon/clay ) cukup mudah untuk mendeteksi perubahan pada ukuran elemen:

var el = new Clay('.element');

el.on('resize', function(size) {
    console.log(size.height, size.width);
});
Zzarcon
sumber
7
Harap berikan pengungkapan yang sesuai mengingat itu adalah perpustakaan Anda.
jhpratt GOFUNDME RELICENSING
Ini bekerja untuk saya, di mana ResizeSensor tidak, itu merusak gaya modal saya. Terima kasih Zzarcon :)
Máxima Alekz
3

Posting blog ini membantu saya mendeteksi perubahan ukuran pada elemen DOM secara efisien.

http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/

Cara menggunakan kode ini ...

AppConfig.addResizeListener(document.getElementById('id'), function () {
  //Your code to execute on resize.
});

Kode paket yang digunakan oleh contoh ...

var AppConfig = AppConfig || {};
AppConfig.ResizeListener = (function () {
    var attachEvent = document.attachEvent;
    var isIE = navigator.userAgent.match(/Trident/);
    var requestFrame = (function () {
        var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
            function (fn) { return window.setTimeout(fn, 20); };
        return function (fn) { return raf(fn); };
    })();

    var cancelFrame = (function () {
        var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
               window.clearTimeout;
        return function (id) { return cancel(id); };
    })();

    function resizeListener(e) {
        var win = e.target || e.srcElement;
        if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
        win.__resizeRAF__ = requestFrame(function () {
            var trigger = win.__resizeTrigger__;
            trigger.__resizeListeners__.forEach(function (fn) {
                fn.call(trigger, e);
            });
        });
    }

    function objectLoad(e) {
        this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
        this.contentDocument.defaultView.addEventListener('resize', resizeListener);
    }

    AppConfig.addResizeListener = function (element, fn) {
        if (!element.__resizeListeners__) {
            element.__resizeListeners__ = [];
            if (attachEvent) {
                element.__resizeTrigger__ = element;
                element.attachEvent('onresize', resizeListener);
            } else {
                if (getComputedStyle(element).position === 'static') element.style.position = 'relative';
                var obj = element.__resizeTrigger__ = document.createElement('object');
                obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
                obj.__resizeElement__ = element;
                obj.onload = objectLoad;
                obj.type = 'text/html';
                if (isIE) element.appendChild(obj);
                obj.data = 'about:blank';
                if (!isIE) element.appendChild(obj);
            }
        }
        element.__resizeListeners__.push(fn);
    };

    AppConfig.removeResizeListener = function (element, fn) {
        element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
        if (!element.__resizeListeners__.length) {
            if (attachEvent) element.detachEvent('onresize', resizeListener);
            else {
                element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
                element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
            }
        }
    }
})();

Catatan: AppConfig adalah namespace / objek yang saya gunakan untuk mengatur fungsi yang dapat digunakan kembali. Jangan ragu untuk mencari dan mengganti nama dengan apa pun yang Anda inginkan.

Ben Gripka
sumber
3

Plugin jQuery saya memungkinkan acara "mengubah ukuran" pada semua elemen, bukan hanya window.

https://github.com/dustinpoissant/ResizeTriggering

$("#myElement") .resizeTriggering().on("resize", function(e){
  // Code to handle resize
}); 
Dustin Poissant
sumber
1
Tautan Anda menunjuk ke lokasi yang tidak ada lagi. Saya telah mencari nama plugin dan menemukan halaman yang sesuai dengan deskripsi Anda. Karena Anda tampaknya menjadi pembuat plugin, saya juga mengedit bahasa jawaban Anda untuk membuatnya jelas bahwa ini adalah pekerjaan Anda. Aturan situs ini adalah bahwa ketika Anda menulis jawaban merekomendasikan menggunakan sesuatu yang Anda kembangkan Anda harus membuat hubungan eksplisit.
Louis
Ini bukan solusi yang bagus karena Anda menjalankan beberapa fungsi di setTimer berulang-ulang yang artinya peramban terus-menerus membakar CPU walaupun tidak ada yang terjadi. Pemilik laptop dan ponsel mendapat manfaat dari solusi yang menggunakan acara browser hanya ketika ada beberapa tindakan, seperti menggunakan acara onScroll.
Roman Zenka
2

Anda dapat mencoba kode di cuplikan berikut, mencakup kebutuhan Anda menggunakan javascript biasa. ( jalankan cuplikan kode dan klik tautan satu halaman penuh untuk memicu peringatan bahwa div diubah ukurannya jika Anda ingin mengujinya. ).

Berdasarkan fakta bahwa ini adalah setInterval100 milidetik, saya berani mengatakan bahwa PC saya tidak merasa terlalu lapar CPU. (0,1% CPU digunakan sebagai total untuk semua tab yang dibuka di Chrome saat diuji.). Tapi sekali lagi ini hanya untuk satu div, jika Anda ingin melakukan ini untuk sejumlah besar elemen maka ya itu bisa sangat lapar CPU.

Anda selalu bisa menggunakan event klik untuk menghentikan div-resize mengendus pula.

var width = 0; 
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
} 
if(document.getElementById("test_div").clientWidth!==width) {   
  alert('resized div');
  width = document.getElementById("test_div").clientWidth;
}    

}, 100);
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" />
        <input type="button" value="button 2" />
        <input type="button" value="button 3" />
</div>

Anda dapat memeriksa biola juga

MEMPERBARUI

var width = 0; 
function myInterval() {
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
} 
if(document.getElementById("test_div").clientWidth!==width) {   
  alert('resized');
  width = document.getElementById("test_div").clientWidth;
}    

}, 100);
return interval;
}
var interval = myInterval();
document.getElementById("clickMe").addEventListener( "click" , function() {
if(typeof interval!=="undefined") {
clearInterval(interval);
alert("stopped div-resize sniffing");
}
});
document.getElementById("clickMeToo").addEventListener( "click" , function() {
myInterval();
alert("started div-resize sniffing");
});
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" id="clickMe" />
        <input type="button" value="button 2" id="clickMeToo" />
        <input type="button" value="button 3" />
</div>

Biola yang Diperbarui

Peter Darmis
sumber
2

Ini adalah salinan tepat dari jawaban teratas, tetapi alih-alih sebuah tautan, itu hanya bagian dari kode yang penting, diterjemahkan menjadi IMO lebih mudah dibaca dan lebih mudah dipahami. Beberapa perubahan kecil lainnya termasuk menggunakan cloneNode (), dan tidak menempatkan html ke string js. Hal-hal kecil, tetapi Anda dapat menyalin dan menempel ini apa adanya dan itu akan berhasil.

Cara kerjanya adalah dengan membuat dua div tak terlihat mengisi elemen yang Anda tonton, lalu menempatkan pemicu di masing-masing, dan mengatur posisi gulir yang akan memicu pemicu perubahan gulir jika ukurannya berubah.

Semua kredit nyata diberikan kepada Marc J, tetapi jika Anda hanya mencari kode yang relevan, ini dia:

    window.El = {}

    El.resizeSensorNode = undefined;
    El.initResizeNode = function() {
        var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
        var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";

        var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
        resizeSensor.style = fillParent;

        var expandSensor = document.createElement("div");
        expandSensor.style = fillParent;
        resizeSensor.appendChild(expandSensor);

        var trigger = document.createElement("div");
        trigger.style = triggerStyle;
        expandSensor.appendChild(trigger);

        var shrinkSensor = expandSensor.cloneNode(true);
        shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
        resizeSensor.appendChild(shrinkSensor);
    }


    El.onSizeChange = function(domNode, fn) {
        if (!domNode) return;
        if (domNode.resizeListeners) {
            domNode.resizeListeners.push(fn);
            return;
        }

        domNode.resizeListeners = [];
        domNode.resizeListeners.push(fn);

        if(El.resizeSensorNode == undefined)
            El.initResizeNode();

        domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
        domNode.appendChild(domNode.resizeSensor);

        var expand = domNode.resizeSensor.firstChild;
        var expandTrigger = expand.firstChild;
        var shrink = domNode.resizeSensor.childNodes[1];

        var reset = function() {
            expandTrigger.style.width = '100000px';
            expandTrigger.style.height = '100000px';

            expand.scrollLeft = 100000;
            expand.scrollTop = 100000;

            shrink.scrollLeft = 100000;
            shrink.scrollTop = 100000;
        };

        reset();

        var hasChanged, frameRequest, newWidth, newHeight;
        var lastWidth = domNode.offsetWidth;
        var lastHeight = domNode.offsetHeight;


        var onResized = function() {
            frameRequest = undefined;

            if (!hasChanged) return;

            lastWidth = newWidth;
            lastHeight = newHeight;

            var listeners = domNode.resizeListeners;
            for(var i = 0; listeners && i < listeners.length; i++) 
                listeners[i]();
        };

        var onScroll = function() {
            newWidth = domNode.offsetWidth;
            newHeight = domNode.offsetHeight;
            hasChanged = newWidth != lastWidth || newHeight != lastHeight;

            if (hasChanged && !frameRequest) {
                frameRequest = requestAnimationFrame(onResized);
            }

            reset();
        };


        expand.addEventListener("scroll", onScroll);
        shrink.addEventListener("scroll", onScroll);
    }
Seph Reed
sumber
1

Solusi Javascript murni, tetapi hanya berfungsi jika elemen diubah ukurannya dengan tombol ubah ukuran css :

  1. ukuran elemen toko dengan offsetWidth dan offsetHeight ;
  2. tambahkan pendengar acara onclick pada elemen ini;
  3. ketika dipicu, bandingkan saat ini offsetWidthdan offsetHeightdengan nilai yang disimpan, dan jika berbeda, lakukan apa yang Anda inginkan dan perbarui nilai-nilai ini.
roipoussiere
sumber
1

Berikut ini adalah versi sederhana dari solusi oleh @nkron, yang berlaku untuk elemen tunggal (bukan array elemen dalam jawaban @ nkron, kompleksitas yang tidak saya butuhkan).

function onResizeElem(element, callback) {    
  // Save the element we are watching
  onResizeElem.watchedElementData = {
    element: element,
    offsetWidth: element.offsetWidth,
    offsetHeight: element.offsetHeight,
    callback: callback
  };

  onResizeElem.checkForChanges = function() {
    const data = onResizeElem.watchedElementData;
    if (data.element.offsetWidth !== data.offsetWidth || data.element.offsetHeight !== data.offsetHeight) {
      data.offsetWidth = data.element.offsetWidth;
      data.offsetHeight = data.element.offsetHeight;
      data.callback();
    }
  };

  // Listen to the window resize event
  window.addEventListener('resize', onResizeElem.checkForChanges);

  // Listen to the element being checked for width and height changes
  onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
  onResizeElem.observer.observe(document.body, {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true
  });
}

Pendengar dan pengamat acara dapat dihapus dengan:

window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
Gman
sumber
0
jQuery(document).ready( function($) {

function resizeMapDIVs() {

// check the parent value...

var size = $('#map').parent().width();



if( $size < 640 ) {

//  ...and decrease...

} else {

//  ..or increase  as necessary

}

}

resizeMapDIVs();

$(window).resize(resizeMapDIVs);

});
Fábio Zangirolami
sumber
0

menggunakan jawaban Bharat Patil cukup kembalikan false di dalam panggilan balik bind Anda untuk mencegah kesalahan tumpukan maksimum, lihat contoh di bawah ini:

$('#test_div').bind('resize', function(){
   console.log('resized');
   return false;
});
kelinci putih
sumber
0

Ini adalah pertanyaan yang sangat lama, tetapi saya pikir saya akan mengirim solusi saya untuk ini.

Saya mencoba menggunakan ResizeSensorkarena semua orang naksir cukup besar. Setelah menerapkan meskipun, saya menyadari bahwa di bawah tenda Element Query membutuhkan elemen yang dimaksud untuk memiliki posisi relativeatauabsolute diterapkan padanya, yang tidak berfungsi untuk situasi saya.

Saya akhirnya menangani ini dengan Rxjs intervalbukannya lurus setTimeoutatau requestAnimationFrameseperti implementasi sebelumnya.

Apa yang baik tentang citarasa interval yang dapat diamati adalah Anda bisa memodifikasi stream, tetapi setiap observasi lainnya dapat diatasi. Bagi saya, implementasi dasar sudah cukup, tetapi Anda bisa menjadi gila dan melakukan segala macam penggabungan, dll.

Dalam contoh di bawah ini, saya melacak perubahan lebar div bagian dalam (hijau). Ini memiliki lebar yang diatur ke 50%, tetapi lebar maks 200px. Menyeret slider mempengaruhi lebar div pembungkus (abu-abu). Anda dapat melihat bahwa api yang dapat diamati hanya menyala ketika lebar div dalam berubah, yang hanya terjadi jika lebar div luar lebih kecil dari 400px.

const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;


const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');




function subscribeToResize() {
  const timer = interval(100);
  const myDiv = document.getElementById('my-div');
  const widthElement = document.getElementById('width');
  const isMax = document.getElementById('is-max');
  
  /*
    NOTE: This is the important bit here 
  */
  timer
    .pipe(
      map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
      distinctUntilChanged(),
      // adding a takeUntil(), here as well would allow cleanup when the component is destroyed
    )
      .subscribe((width) => {        
        widthElement.innerHTML = width;
        isMax.innerHTML = width === 200 ? 'Max width' : '50% width';

      });
}

function defineRange() {
  input.min = 200;
  input.max = window.innerWidth;
  input.step = 10;
  input.value = input.max - 50;
}

function bindInputToWrapper() {
  input.addEventListener('input', (event) => {
    wrapper.style.width = `${event.target.value}px`;
  });
}

defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
  width: 50%;
  max-width: 200px;
}




/* Aesthetic styles only */

.inner {
  background: #16a085;
}

.wrapper {
  background: #ecf0f1;
  color: white;
  margin-top: 24px;
}

.content {
  padding: 12px;
}

body {
  
  font-family: sans-serif;
  font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

<h1>Resize Browser width</h1>

<label for="width-input">Adjust the width of the wrapper element</label>
<div>
  <input type="range" id="width-input">
</div>

<div id="my-wrapper" class="wrapper">
  <div id="my-div" class="inner">
    <div class="content">
      Width: <span id="width"></span>px
      <div id="is-max"></div>  
    </div>
  </div>
</div>

Scrimothy
sumber
-1

Hanya Window.onResizeada dalam spesifikasi, tetapi Anda selalu dapat memanfaatkan IFrameuntuk menghasilkan Windowobjek baru di dalam DIV Anda.

Silakan periksa jawaban ini . Ada plugin jquery kecil baru , yang portabel dan mudah digunakan. Anda selalu dapat memeriksa kode sumber untuk melihat bagaimana hal itu dilakukan.

<!-- (1) include plugin script in a page -->
<script src="/src/jquery-element-onresize.js"></script>

// (2) use the detectResizing plugin to monitor changes to the element's size:
$monitoredElement.detectResizing({ onResize: monitoredElement_onResize });

// (3) write a function to react on changes:
function monitoredElement_onResize() {    
    // logic here...
}
rbtbar
sumber
Ini adalah solusi yang sangat tidak efektif jika Anda memiliki div yang bisa diubah ukurannya.
suricactus
-1

saya pikir itu tidak dapat dilakukan tetapi kemudian saya memikirkannya, Anda dapat secara manual mengubah ukuran div melalui style = "resize: both;" untuk melakukan itu Anda harus mengkliknya sehingga menambahkan fungsi onclick untuk memeriksa tinggi dan lebar elemen dan berfungsi. Dengan hanya 5 baris javascript murni (tentu bisa lebih pendek) http://codepen.io/anon/pen/eNyyVN

<div id="box" style="
                height:200px; 
                width:640px;
                background-color:#FF0066;
                resize: both;
                overflow: auto;" 
                onclick="myFunction()">
    <p id="sizeTXT" style="
                font-size: 50px;">
       WxH
    </p>
    </div>

<p>This my example demonstrates how to run a resize check on click for resizable div.</p>

<p>Try to resize the box.</p>


<script>
function myFunction() {
var boxheight = document.getElementById('box').offsetHeight;
var boxhwidth = document.getElementById('box').offsetWidth;
var txt = boxhwidth +"x"+boxheight;
document.getElementById("sizeTXT").innerHTML = txt;
}
</script>
PixelPimp
sumber
2
Ini hanya menghitung ketika acara klik dipecat yang bukan merupakan bagian dari persyaratan pertanyaan.
Rob Evans