เหตุการณ์การปรับขนาดหน้าต่าง JavaScript


616

ฉันจะขอปรับขนาดเหตุการณ์หน้าต่างปรับขนาดเบราว์เซอร์ได้อย่างไร

มีวิธี jQuery ในการฟังสำหรับปรับขนาดกิจกรรมแต่ฉันไม่ต้องการที่จะนำมาไว้ในโครงการของฉันสำหรับข้อกำหนดนี้


8
ขอบคุณทุกคน ฉันไม่สนใจเกี่ยวกับ IE ฉันกำลังคิดเกี่ยวกับการปรับขนาดโอเปร่าบนโทรศัพท์มือถือ
บัญชีที่ตาย

คำตอบ:


641

jQuery เป็นเพียงห่อresizeเหตุการณ์ DOM มาตรฐานเช่น

window.onresize = function(event) {
    ...
};

jQuery อาจทำงานเพื่อให้แน่ใจว่าเหตุการณ์การปรับขนาดได้รับการยิงอย่างสม่ำเสมอในเบราว์เซอร์ทั้งหมด แต่ฉันไม่แน่ใจว่าเบราว์เซอร์ใด ๆ ที่แตกต่างกัน แต่ฉันขอแนะนำให้คุณทดสอบใน Firefox, Safari และ IE


226
gotcha ที่อาจเกิดขึ้นด้วยวิธีนี้คือคุณกำลังเอาชนะเหตุการณ์ดังนั้นคุณจึงไม่สามารถแนบการกระทำหลายรายการกับกิจกรรมได้ คำสั่งผสม addEventListener / attachEvent เป็นวิธีที่ดีที่สุดในการทำให้ javascript ของคุณเล่นได้ง่ายกับสคริปต์อื่น ๆ ที่อาจเรียกใช้บนหน้าเว็บ
MyItchyChin

4
var onresize = window.onresize; window.onresize = function (event) {ถ้า (typeof onresize === 'function') onresize (); / ** ... * /}
SubOne

230
window.addEventListener('resize', function(){}, true);
WebWanderer

16
@ SubOne เป็นตัวอย่างที่สมบูรณ์แบบของวิธีที่ไม่ควรทำ
Eugene Pankov

28
ECMAScript 6 : window.onresize = () => { /* code */ };หรือดีกว่า:window.addEventListener('resize', () => { /* code */ });
Derk Jan Speelman

560

ก่อนอื่นฉันรู้ว่าaddEventListenerวิธีการดังกล่าวได้รับการกล่าวถึงในความคิดเห็นข้างต้น แต่ฉันไม่เห็นรหัสใด ๆ เนื่องจากเป็นวิธีที่ต้องการนี่คือ:

window.addEventListener('resize', function(event){
  // do stuff here
});

นี่คือตัวอย่างการทำงาน


63
นี่คือคำตอบที่ตรงไปตรงมาที่สุด
jacoviza

7
เสียงเหมือนคำตอบที่ดีที่สุดเพราะคุณไม่ได้เขียนทับหน้าต่าง window.onresize event :-)
James Harrington

27
หลายคนเพิ่มผู้ฟังเหตุการณ์ที่ไม่ระบุชื่ออย่างที่คุณทำในตัวอย่างนี้ แต่มันไม่ใช่วิธีปฏิบัติที่ดีจริงๆเพราะวิธีการนี้ทำให้ไม่สามารถลบผู้ฟังเหล่านี้ได้ในภายหลัง สิ่งนี้เคยเป็นปัญหาเพราะหน้าเว็บมีอายุสั้นแล้ว แต่ในวันนี้และอายุของ AJAX และ RIAs และ SPA มันกำลังเป็นที่หนึ่ง เราต้องการวิธีจัดการวงจรการรับฟังเหตุการณ์ removeEventListenerดังนั้นมันจะดีกว่าที่จะออกมาจากปัจจัยที่ฟังก์ชั่นเข้ากับฟังก์ชั่นที่แยกต่างหากเพื่อที่จะสามารถลบออกได้ในภายหลังด้วย
Stijn de Witt

6
ทำไมไม่สามารถตอบทุกคนเช่นนี้โดยไม่ต้องทั้งหมดปุย
quemeful

2
ฉันคิดว่ามีความจริงทั้งคำตอบของ Stijn de Witt และ quemeful ที่นี่ บางครั้งคุณสนใจที่จะลบผู้ฟังเหตุการณ์ แต่ในกรณีที่คุณไม่เพิ่มรหัสพิเศษทั้งหมดในตัวอย่างด้านบนนั้นไม่จำเป็นและอาจทำให้เกิดการบวมและอ่านง่าย ในความคิดของฉันถ้าคุณไม่นึกภาพถึงเหตุผลที่ต้องลบผู้ฟังวิธีนี้เป็นทางออกที่ดีที่สุด คุณสามารถเขียนใหม่ได้ในภายหลังหากจำเป็น จากประสบการณ์ของฉันความต้องการเหล่านี้เกิดขึ้นในสถานการณ์เฉพาะเท่านั้น
cazort

525

อย่าแทนที่ฟังก์ชั่น window.onresize

ให้สร้างฟังก์ชันเพื่อเพิ่ม Event Listener ให้กับวัตถุหรือองค์ประกอบแทน สิ่งนี้จะตรวจสอบและใส่หีบที่ผู้ฟังไม่ทำงานจากนั้นจะแทนที่ฟังก์ชั่นของวัตถุเป็นทางเลือกสุดท้าย นี่เป็นวิธีที่ต้องการใช้ในไลบรารีเช่น jQuery

object: องค์ประกอบหรือวัตถุหน้าต่าง
type: ปรับขนาดเลื่อน (ประเภทเหตุการณ์)
callback: การอ้างอิงฟังก์ชั่น

var addEvent = function(object, type, callback) {
    if (object == null || typeof(object) == 'undefined') return;
    if (object.addEventListener) {
        object.addEventListener(type, callback, false);
    } else if (object.attachEvent) {
        object.attachEvent("on" + type, callback);
    } else {
        object["on"+type] = callback;
    }
};

จากนั้นใช้เป็นเช่นนี้:

addEvent(window, "resize", function_reference);

หรือด้วยฟังก์ชั่นที่ไม่ระบุชื่อ:

addEvent(window, "resize", function(event) {
  console.log('resized');
});

100
นี่ควรเป็นคำตอบที่ยอมรับได้ การเอาชนะการทบทวนเป็นเพียงการปฏิบัติที่ไม่ดี
Tiberiu-Ionuț Stan

8
ฮะมีการตรวจสอบโมฆะพิเศษทั้งหมดคืออะไร พยายามที่จะทำงานใน IE 4.5 หรืออะไร?
FlavourScape

1
และคุณสงสัยว่าฉันจะเรียกใช้ฟังก์ชันนี้เพื่อรับหน้าต่างปรับขนาดเหตุการณ์ได้อย่างไร นี่คือวิธี: addEvent (หน้าต่าง "ปรับขนาด", function_reference); :)
oabarca

6
โปรดทราบว่าตั้งแต่ IE 11 attachEventไม่รองรับอีกต่อไป addEventListenerวิธีที่แนะนำสำหรับอนาคตคือ
ลุค

6
elem == undefinedโปรดใช้ความระมัดระวังด้วย เป็นไปได้ (แม้ว่าไม่น่าเป็นไปได้) ว่า "undefined" ถูกกำหนดแบบโลคัลเป็นอย่างอื่น stackoverflow.com/questions/8175673/…
ลุค

32

ไม่ควรใช้เหตุการณ์การปรับขนาดโดยตรงเนื่องจากถูกปรับขนาดอย่างต่อเนื่องเมื่อเราปรับขนาด

ใช้ฟังก์ชั่น debounce เพื่อลดการโทรเกิน

window.addEventListener('resize',debounce(handler, delay, immediate),false);

นี่คือ debounce ทั่วไปที่ลอยอยู่ในเน็ตแม้ว่าจะมองหาตัวละครที่ล้ำหน้ากว่านี้ในฐานะ featuerd

const debounce = (func, wait, immediate) => {
    var timeout;
    return () => {
        const context = this, args = arguments;
        const later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

สามารถใช้งานได้เช่นนี้ ...

window.addEventListener('resize', debounce(() => console.log('hello'),
200, false), false);

มันจะไม่ยิงมากกว่าหนึ่งครั้งทุกๆ 200 มิลลิวินาที

สำหรับการเปลี่ยนการวางแนวมือถือใช้:

window.addEventListener('orientationchange', () => console.log('hello'), false);

นี่คือห้องสมุดเล็ก ๆ ที่ฉันรวบรวมดูแลอย่างเรียบร้อย


ฉันขอขอบคุณข้อมูลที่: debounce (ตอนนี้ดูเหมือนว่าแนวคิด / ฟังก์ชั่นที่ประเมินค่าไม่ได้แม้ว่าฉันเคยต่อต้านการใช้ timeouts ในลักษณะนี้), lodash (แม้ว่าฉันจะไม่เชื่อโดยส่วนตัว) และแก้ไขความคลุมเครือเกี่ยวกับการใช้ addEvent เป็นทางเลือก เพื่อ addEventListener (เพียงเพราะคำตอบเก่าไม่ได้จริงๆอย่างชัดเจนอยู่การแสดงความคิดเห็นเกี่ยวกับเรื่องนี้)
wunth

5
FYI เนื่องจากคุณใช้สัญกรณ์ลูกศร ES6 ฟังก์ชั่นด้านในจะต้องมี(...arguments) => {...}(หรือ...argsสำหรับความสับสนน้อยลง) เนื่องจากargumentsตัวแปรไม่สามารถใช้งานได้อีกต่อไป
coderatchet

ฉันรู้อย่างเต็มที่ว่าข้อมูลนั้นไม่ใช่รหัสของฉันมันเป็นเพียงตัวอย่าง
frontsideup

คำตอบที่ดีปัญหาเกี่ยวกับการปรับขนาดต้องได้รับการแก้ไข! การเปิดตัวเป็นตัวเลือกอีกวิธีหนึ่งคือการควบคุมปริมาณอย่างง่าย (ซึ่งอาจเป็นวิธีที่จะไปหากคุณต้องการให้ UI ของคุณปรับขนาดใน "ขั้นตอน") ดูbencentra.com/code/2015/02/27/optimizing-window-resize.htmlสำหรับตัวอย่าง
Robin Métral

24

คำตอบสำหรับ 2018+:

คุณควรใช้ResizeObserver มันเป็นวิธีแก้ปัญหาเบราว์เซอร์ที่มีประสิทธิภาพดีกว่าการใช้resizeเหตุการณ์ นอกจากนี้มันไม่เพียง แต่สนับสนุนเหตุการณ์ที่แต่ยังเกี่ยวกับพลdocumentelements

var ro = new ResizeObserver( entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;
    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
  }
});

// Observe one or multiple elements
ro.observe(someElement);

ปัจจุบันFirefox, Chrome และ Safari สนับสนุนมัน เบราว์เซอร์อื่น ๆ (และเก่า) คุณต้องใช้polyfill


ยังเป็นคำตอบที่ดีที่สุดในปี 2020 imo
Jesse Reza Khorasanee


16

ฉันเชื่อว่าคำตอบที่ถูกต้องนั้นได้รับการจัดหาโดย @Alex V แล้ว แต่คำตอบนั้นจำเป็นต้องได้รับการปรับปรุงให้ทันสมัยเนื่องจากอายุมากกว่าห้าปีในขณะนี้

มีสองประเด็นหลัก:

  1. ห้ามใช้objectเป็นชื่อพารามิเตอร์ มันเป็นคำที่ reservered ด้วยความเป็นอยู่นี้กล่าวว่า @ Alex V strict modeของฟังก์ชั่นที่ให้บริการจะไม่ทำงานใน

  2. addEventฟังก์ชั่นให้โดย @ Alex V ไม่กลับevent objectถ้าaddEventListenerวิธีการที่ใช้ ควรเพิ่มพารามิเตอร์อื่นลงในaddEventฟังก์ชันเพื่ออนุญาตสิ่งนี้

หมายเหตุ: พารามิเตอร์ใหม่ที่addEventถูกสร้างขึ้นเป็นทางเลือกเพื่อให้การย้ายไปยังฟังก์ชันรุ่นใหม่นี้จะไม่ทำให้การเรียกใช้ฟังก์ชันนี้ก่อนหน้านี้แตก การใช้งานแบบดั้งเดิมทั้งหมดจะได้รับการสนับสนุน

นี่คือaddEventฟังก์ชั่นที่ได้รับการอัพเดตพร้อมการเปลี่ยนแปลงเหล่านี้:

/*
    function: addEvent

    @param: obj         (Object)(Required)

        -   The object which you wish
            to attach your event to.

    @param: type        (String)(Required)

        -   The type of event you
            wish to establish.

    @param: callback    (Function)(Required)

        -   The method you wish
            to be called by your
            event listener.

    @param: eventReturn (Boolean)(Optional)

        -   Whether you want the
            event object returned
            to your callback method.
*/
var addEvent = function(obj, type, callback, eventReturn)
{
    if(obj == null || typeof obj === 'undefined')
        return;

    if(obj.addEventListener)
        obj.addEventListener(type, callback, eventReturn ? true : false);
    else if(obj.attachEvent)
        obj.attachEvent("on" + type, callback);
    else
        obj["on" + type] = callback;
};

ตัวอย่างการเรียกใช้addEventฟังก์ชันใหม่:

var watch = function(evt)
{
    /*
        Older browser versions may return evt.srcElement
        Newer browser versions should return evt.currentTarget
    */
    var dimensions = {
        height: (evt.srcElement || evt.currentTarget).innerHeight,
        width: (evt.srcElement || evt.currentTarget).innerWidth
    };
};

addEvent(window, 'resize', watch, true);

ฉันจะยืนยันว่า AttachEvent ไม่มีความเกี่ยวข้องอีกต่อไปในปี 2560
วลานิโคลา

@VladNicula ฉันเห็นด้วย แต่คำตอบนี้มีอายุสองปี
WebWanderer

12

ขอบคุณสำหรับการอ้างอิงโพสต์บล็อกของฉันที่http://mbccs.blogspot.com/2007/11/fixing-window-resize-event-in-ie.html

ในขณะที่คุณสามารถเชื่อมต่อกับเหตุการณ์การปรับขนาดหน้าต่างมาตรฐานคุณจะพบว่าใน IE เหตุการณ์จะถูกยิงหนึ่งครั้งสำหรับทุก ๆ X และอีกครั้งสำหรับการเคลื่อนไหวแกน Y ทุกครั้งทำให้เกิดเหตุการณ์มากมายที่อาจมีประสิทธิภาพ ส่งผลกระทบต่อเว็บไซต์ของคุณหากการแสดงผลเป็นงานที่ต้องทำมาก

วิธีการของฉันเกี่ยวข้องกับการหมดเวลาสั้น ๆ ที่ถูกยกเลิกในเหตุการณ์ที่ตามมาเพื่อให้เหตุการณ์ไม่ได้รับการขัดเกลารหัสของคุณจนกว่าผู้ใช้จะปรับขนาดหน้าต่างเสร็จสิ้น


7
window.onresize = function() {
    // your code
};

22
ดังที่ความคิดเห็นหลายข้อข้างต้นบอกว่าจะเป็นการดีที่สุดที่จะไม่เขียนทับฟังก์ชัน onresize ค่อนข้างจะเพิ่มกิจกรรมใหม่ ดูคำตอบของ Jondlm
ลุค

6

โพสต์บล็อกต่อไปนี้อาจเป็นประโยชน์กับคุณ: การแก้ไขเหตุการณ์การปรับขนาดหน้าต่างใน IE

มันให้รหัสนี้:

Sys.Application.add_load(function(sender, args) {
    $addHandler(window, 'resize', window_resize);
});

var resizeTimeoutId;

function window_resize(e) {
     window.clearTimeout(resizeTimeoutId);
     resizeTimeoutId = window.setTimeout('doResizeCode();', 10);
}

6
อันนี้ใช้ได้กับแอปพลิเคชัน ASP.NET เท่านั้น
Evgeny Gorb

2

วิธีแก้ไขปัญหาที่กล่าวถึงข้างต้นจะใช้งานได้หากสิ่งที่คุณต้องการทำคือปรับขนาดหน้าต่างและหน้าต่างเท่านั้น อย่างไรก็ตามหากคุณต้องการปรับขนาดที่แพร่กระจายไปยังองค์ประกอบลูกคุณจะต้องเผยแพร่กิจกรรมด้วยตัวเอง นี่คือตัวอย่างรหัสที่จะทำ:

window.addEventListener("resize", function () {
  var recResizeElement = function (root) {
    Array.prototype.forEach.call(root.childNodes, function (el) {

      var resizeEvent = document.createEvent("HTMLEvents");
      resizeEvent.initEvent("resize", false, true);
      var propagate = el.dispatchEvent(resizeEvent);

      if (propagate)
        recResizeElement(el);
    });
  };
  recResizeElement(document.body);
});

โปรดทราบว่าองค์ประกอบลูกสามารถโทร

 event.preventDefault();

บนวัตถุเหตุการณ์ที่ส่งผ่านเป็น Arg แรกของเหตุการณ์ปรับขนาด ตัวอย่างเช่น:

var child1 = document.getElementById("child1");
child1.addEventListener("resize", function (event) {
  ...
  event.preventDefault();
});


1
var EM = new events_managment();

EM.addEvent(window, 'resize', function(win,doc, event_){
    console.log('resized');
    //EM.removeEvent(win,doc, event_);
});

function events_managment(){
    this.events = {};
    this.addEvent = function(node, event_, func){
        if(node.addEventListener){
            if(event_ in this.events){
                node.addEventListener(event_, function(){
                    func(node, event_);
                    this.events[event_](win_doc, event_);
                }, true);
            }else{
                node.addEventListener(event_, function(){
                    func(node, event_);
                }, true);
            }
            this.events[event_] = func;
        }else if(node.attachEvent){

            var ie_event = 'on' + event_;
            if(ie_event in this.events){
                node.attachEvent(ie_event, function(){
                    func(node, ie_event);
                    this.events[ie_event]();
                });
            }else{
                node.attachEvent(ie_event, function(){
                    func(node, ie_event);
                });
            }
            this.events[ie_event] = func;
        }
    }
    this.removeEvent = function(node, event_){
        if(node.removeEventListener){
            node.removeEventListener(event_, this.events[event_], true);
            this.events[event_] = null;
            delete this.events[event_];
        }else if(node.detachEvent){
            node.detachEvent(event_, this.events[event_]);
            this.events[event_] = null;
            delete this.events[event_];
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.