JQuery: จะเรียกเหตุการณ์ปรับขนาดได้อย่างไรเมื่อปรับขนาดเสร็จแล้ว


109

ฉันจะเรียกใช้ฟังก์ชันได้อย่างไรเมื่อหน้าต่างเบราว์เซอร์ปรับขนาดเสร็จแล้ว

ฉันกำลังพยายามทำเช่นนั้น แต่กำลังมีปัญหา ฉันใช้ฟังก์ชันเหตุการณ์ JQuery Resize:

$(window).resize(function() {
  ... // how to call only once the browser has FINISHED resizing?
});

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

ฉันจะเรียกฟังก์ชั่นปรับขนาดเพียงครั้งเดียวได้อย่างไร (เมื่อปรับขนาดหน้าต่างเบราว์เซอร์เสร็จแล้ว)

อัปเดต

โดยไม่ต้องใช้ตัวแปรส่วนกลาง


@BGerrissen ถ้าคุณสามารถแสดงวิธีทำjsfiddle.net/Zevan/c9UE5/1โดยไม่มีตัวแปรส่วนกลางฉันจะทำอย่างแน่นอน :)
nickb

วิธี cleartimeout / settimeout ข้างต้นทำงานได้ดีอย่างน่าอัศจรรย์
kris-o3

คำตอบ:


129

นี่คือตัวอย่างโดยใช้คำแนะนำของ thejh

คุณสามารถจัดเก็บ id อ้างอิงไว้ที่ setInterval หรือ setTimeout แบบนี้:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);

ฉันชอบที่รหัสนี้เรียบง่ายมาก มีวิธีใดบ้างที่จะทำให้งานนี้โดยไม่ต้องใช้ตัวแปรส่วนกลาง
nickb

ถ้าฉันเห็นวิธีทำโดยไม่มีตัวแปรส่วนกลางฉันจะทำให้เป็น "คำตอบที่ยอมรับ"
nickb

ไม่มีปัญหา. มีไม่กี่วิธี ฉันได้แก้ไขคำตอบเพื่อสะท้อนคำตอบที่ง่ายที่สุดที่ฉันคิดได้
Zevan

ขอบคุณสำหรับการอัปเดต แต่ดูเหมือนว่าจะยังคงใช้ตัวแปรส่วนกลาง คุณสามารถอัปเดตเพื่อไม่ต้องใช้ตัวแปรส่วนกลาง (var id) ขอบคุณ
nickb

4
ฉันคิดว่าคุณพลาดลิงก์ในตอนท้ายของโพสต์ ... ลองดู: jsfiddle.net/Zevan/c9UE5/5
Zevan

85

debounce

function debouncer( func , timeout ) {
   var timeoutID , timeout = timeout || 200;
   return function () {
      var scope = this , args = arguments;
      clearTimeout( timeoutID );
      timeoutID = setTimeout( function () {
          func.apply( scope , Array.prototype.slice.call( args ) );
      } , timeout );
   }
}


$( window ).resize( debouncer( function ( e ) {
    // do stuff 
} ) );

หมายเหตุคุณสามารถใช้วิธีนี้สำหรับทุกสิ่งที่คุณต้องการ debounce (เหตุการณ์สำคัญ ฯลฯ )

ปรับแต่งพารามิเตอร์การหมดเวลาเพื่อให้ได้เอฟเฟกต์ที่ต้องการอย่างเหมาะสมที่สุด


1
ฉันแค่พยายามทำงานนี้และดูเหมือนว่ามันจะทำงานสองครั้ง ฉันแทนที่"// do stuff"ด้วย"$ (" body ") ผนวก (" <br/> DONE! ");" และจะเรียกมันสองครั้งในการปรับขนาดเบราว์เซอร์
nickb

โอ๊ะโอ ... ฉันลืมส่วนที่ค่อนข้างสำคัญไป (ตั้งค่า timeoutID) ... แก้ไขแล้วโปรดลองอีกครั้ง;)
BGerrissen

3
@ user43493: มันเรียก cuntion funcโดยthisตัวชี้ภายในชี้ไปที่scopeและมีArray.prototype.slice.call(args)(ซึ่งสร้างอาร์เรย์มาตรฐานออกมาargs) เป็นอาร์กิวเมนต์
Eric

4
นี่เป็นนามธรรมที่ใช้ซ้ำได้ดังนั้นคุณจึงไม่จำเป็นต้องใช้รหัสการหมดเวลาด้วยมือหรือติดตามรหัสหมดเวลาทั่วโลกด้วยตัวคุณเอง คุณสามารถใช้มันเพื่อเพิ่มขนาดหน้าต่าง;) ตัวอย่างเช่นเพื่อ debounce ปุ่มส่งโดยส่งผ่านพารามิเตอร์การหมดเวลาที่สูงขึ้น คุณสามารถเลือกใช้โซลูชันโค้ดน้อยลงได้ แต่ฉันขอแนะนำให้คุณเก็บตัวอย่างนี้ไว้ในชุดของคุณคุณจะประทับใจในภายหลัง;)
BGerrissen

2
Underscore.js มีการใช้งานที่ดีหากคุณใช้ lib นั้นอยู่แล้ว underscorejs.org/#debounce
twmulloy

22

คุณสามารถใช้setTimeout()และclearTimeout()ร่วมกับjQuery.data:

$(window).resize(function() {
    clearTimeout($.data(this, 'resizeTimer'));
    $.data(this, 'resizeTimer', setTimeout(function() {
        //do something
        alert("Haven't resized in 200ms!");
    }, 200));
});

อัปเดต

ฉันเขียนส่วนขยายเพื่อปรับปรุงค่าเริ่มต้นon(& bind) -event-handler ของ jQuery จะแนบฟังก์ชันตัวจัดการเหตุการณ์สำหรับเหตุการณ์อย่างน้อยหนึ่งเหตุการณ์เข้ากับองค์ประกอบที่เลือกหากเหตุการณ์ไม่ได้ถูกทริกเกอร์ในช่วงเวลาที่กำหนด สิ่งนี้มีประโยชน์หากคุณต้องการเริ่มการเรียกกลับหลังจากเกิดความล่าช้าเท่านั้นเช่นเหตุการณ์ปรับขนาดหรืออื่น ๆ https://github.com/yckart/jquery.unevent.js

;(function ($) {
    var methods = { on: $.fn.on, bind: $.fn.bind };
    $.each(methods, function(k){
        $.fn[k] = function () {
            var args = [].slice.call(arguments),
                delay = args.pop(),
                fn = args.pop(),
                timer;

            args.push(function () {
                var self = this,
                    arg = arguments;
                clearTimeout(timer);
                timer = setTimeout(function(){
                    fn.apply(self, [].slice.call(arg));
                }, delay);
            });

            return methods[k].apply(this, isNaN(delay) ? arguments : args);
        };
    });
}(jQuery));

ใช้มันเหมือนกับตัวจัดการอื่น ๆonหรือbind-event ยกเว้นว่าคุณสามารถส่งผ่านพารามิเตอร์พิเศษเป็นตัวสุดท้าย:

$(window).on('resize', function(e) {
    console.log(e.type + '-event was 200ms not triggered');
}, 200);

http://jsfiddle.net/ARTsinn/EqqHx/


1
ทำการค้นหาโดย Google เหมือนเดิมทุกวัน และมาที่หน้าเดียวกันสำหรับโค้ดชิ้นนี้ หวังว่าฉันจะจำมันได้ฮ่า ๆ
Dustin Silk

7
var lightbox_resize = false;
$(window).resize(function() {
    console.log(true);
    if (lightbox_resize)
        clearTimeout(lightbox_resize);
    lightbox_resize = setTimeout(function() {
        console.log('resize');
    }, 500);
});

ฉันชอบสิ่งนี้มากที่สุด แต่ทำไมไม่ใช้วงเล็บปีกกากับข้อความที่มีเงื่อนไขของคุณ ฉันรู้ว่ามันใช้งานได้หากไม่มีพวกเขา แต่มันเป็นความเจ็บปวดสำหรับนักพัฒนาคนอื่น ๆ ที่ต้องมอง;)
teewuane

7

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

function registerResize(f) {
    $(window).resize(function() {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(function() {
            var oldOverflow = document.body.style.overflow;
            document.body.style.overflow = "hidden";
            var currHeight = $(window).height(),
                currWidth = $(window).width();
            document.body.style.overflow = oldOverflow;

            var prevUndefined = (typeof this.prevHeight === 'undefined' || typeof this.prevWidth === 'undefined');
            if (prevUndefined || this.prevHeight !== currHeight || this.prevWidth !== currWidth) {
                //console.log('Window size ' + (prevUndefined ? '' : this.prevHeight + "," + this.prevWidth) + " -> " + currHeight + "," + currWidth);
                this.prevHeight = currHeight;
                this.prevWidth = currWidth;

                f(currHeight, currWidth);
            }
        }, 200);
    });
    $(window).resize(); // initialize
}

registerResize(function(height, width) {
    // this will be called only once per resize regardless of scrollbars changes
});

ดูjsfiddle


5

Underscore.jsมีสองวิธีที่ยอดเยี่ยมสำหรับงานนี้: throttleและdebounce. แม้ว่าคุณจะไม่ได้ใช้ Underscore ให้ดูที่มาของฟังก์ชันเหล่านี้ นี่คือตัวอย่าง:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);

2

นี่คือแนวทางของฉัน:

document.addEventListener('DOMContentLoaded', function(){
    var tos = {};
    var idi = 0;
    var fn  = function(id)
    {
        var len = Object.keys(tos).length;

        if(len == 0)
            return;

        to = tos[id];
        delete tos[id];

        if(len-1 == 0)
            console.log('Resize finished trigger');
    };

    window.addEventListener('resize', function(){
        idi++;
        var id = 'id-'+idi;
        tos[id] = window.setTimeout(function(){fn(id)}, 500);
    });
});

resize-event-listener จับการเรียกปรับขนาดที่เข้ามาทั้งหมดสร้างฟังก์ชันการหมดเวลาสำหรับแต่ละรายการและบันทึกตัวระบุการหมดเวลาพร้อมกับหมายเลขซ้ำที่นำหน้าด้วย'id-'(เพื่อใช้เป็นคีย์อาร์เรย์ได้) ในtos-array

ทุกครั้งไทม์เอาต์จะทริกเกอร์เรียกว่าfnฟังก์ชันซึ่งจะตรวจสอบว่าเป็นการหมดเวลาสุดท้ายในtosอาร์เรย์หรือไม่ (ฟังก์ชัน fn จะลบทุกไทม์เอาต์ที่ดำเนินการ) ถ้าเป็นจริง (= if(len-1 == 0)) การปรับขนาดจะเสร็จสิ้น


คงจะดีไม่น้อยหากคุณมีความคิดเห็นหรือคำอธิบายเกี่ยวกับสิ่งที่คุณกำลังทำอยู่ที่นี่
Brett Gregson

1

jQuery มีoffวิธีการลบตัวจัดการเหตุการณ์

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.