Як дочекатися закінчення події "змінити розмір" і лише потім виконати дію?


243

Тому я зараз використовую щось на зразок:

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

Але це називається багато разів, поки триває процес зміни розміру. Чи можливо зловити подію, коли вона закінчиться?


Можливо, приєднайте за допомогою, .one()щоб воно виконувалося лише після того, як буде зроблено всі зміни розміру, а не знову і знову?
Бред Крісті

5
Коли користувач змінює розмір вікна вручну (перетягнувши його), подія зміни розміру буде викликатися не один раз, тому використання .one () дійсно не буде ефективним.
jessegavin

Використання анонімної функції у вищезазначеному може бути знято для простоти та граничної швидкості: $ (window) .resize (resizedw)
Fornost

Ось для цього бібліотека jQuery: github.com/nielse63/jquery.resizeend
rugk

Відповіді:


177

Мені пощастило з такою рекомендацією: http://forum.jquery.com/topic/the-resizeend-event

Ось код, щоб вам не довелося копати посилання та джерело його публікації:

var rtime;
var timeout = false;
var delta = 200;
$(window).resize(function() {
    rtime = new Date();
    if (timeout === false) {
        timeout = true;
        setTimeout(resizeend, delta);
    }
});

function resizeend() {
    if (new Date() - rtime < delta) {
        setTimeout(resizeend, delta);
    } else {
        timeout = false;
        alert('Done resizing');
    }               
}

Дякую sime.vidas за код!


1
Можливо, вам захочеться змінити Дату на щось на зразок new Date(-1E12)- тобто JSLint попереджає про використання 00.
elundmark

Дякую клеймо. Я переключив інстанціювання дати використовувати одиничні 0; сподіваємось, що скарга не породжує.
Долан Антенуччі

@elundmark або використовувати + операцію. rtime: Date; .... if (+new Date() - +rtime < delta)а в typecript resizeend функція повинна бути такою, як ця стрілка resizeend=()=>. Тому що у функції resizeend thisпосилання на об’єкт вікна.
Muhammet Can TONBUL

517

Ви можете використовувати setTimeout()іclearTimeout()

function resizedw(){
    // Haven't resized in 100ms!
}

var doit;
window.onresize = function(){
  clearTimeout(doit);
  doit = setTimeout(resizedw, 100);
};

Приклад коду на jsfiddle .


7
Це чудова відповідь. Він робить те, що робить плагін, який я рекомендував, тільки без плагіна.
jessegavin

Я думаю, що єдиний спосіб реально покращити це - виявити рух миші. Я підозрюю, що копатися в ньому не оправдається.
Майкл Харен

Чи працює це лише в тому випадку, якщо зміна розміру закінчена протягом секунди? Моя функція спрацьовувала, коли я намагався використовувати це (я повільно не
змінював

@MichaelHaren Оскільки ручка повторного розміру, як правило, знаходиться поза межами $(document), виявлення миші обмежене лише користувачами під керуванням Microsoft Windows та вразливими версіями її Internet Explorer: iedataleak.spider.io/demo
Alastair

12
Це дуже проста реалізація концепції дебютування ( unscriptable.com/2009/03/20/debouncing-javascript-methods ). Пол Ірланд (та інші) запропонував набагато ефективніше рішення, яке не обробляє події "зайвих" змін
rmoestl

78

Це код, який я пишу відповідно до відповіді @Mark Coleman:

$(window).resize(function() {
    clearTimeout(window.resizedFinished);
    window.resizedFinished = setTimeout(function(){
        console.log('Resized finished.');
    }, 250);
});

Дякую Марку!


1
Приємний підхід. Також згадується тут з тією різницею , що ніякі змін в супер змінні вікна не виробляються.
Альвін Кеслер

2
@AlwinKesler - у вашому прикладі змінна resizeTimerє глобальною змінною, це означає, що вона визначена no the window, тому вона точно така ж, як і тут, тільки цей приклад краще, оскільки вам не потрібно визначати змінну зовні. і також має сенс додати цю змінну до windowоб'єкта, оскільки це Об'єкт, до якого прив'язується слухач подій.
vsync

1
Дякую! Просто хотілося додати, що в деяких випадках для виконання певних завдань у зворотному дзвінку потрібен більший інтервал часу. Наприклад, у моєму випадку 250 не працювало, але 700 працювали чудово.
Марія Блер

Найприємніше рішення.
Даніель

36

Internet Explorer надає подію resizeEnd . Інші веб-переглядачі будуть багато разів запускати подію зміни розміру під час зміни розміру.

Тут є інші чудові відповіді, які показують, як використовувати setTimeout та .throttle ,.debounce методи від lodash і підкреслюють, тому я згадаю плагін jQuery з дросельним дебютом Бен Альмана, який виконує те, що ви хочете.

Припустимо, у вас є ця функція, яку ви хочете запустити після зміни розміру:

function onResize() {
  console.log("Resize just happened!");
};

Приклад дросельної заслінки
У наступному прикладі onResize()буде викликатися лише раз на 250 мілісекунд під час зміни розміру вікна.

$(window).resize( $.throttle( 250, onResize) );

Приклад дебютації
У наступному прикладі onResize()буде викликано лише один раз у кінці дії зміни розміру вікна. Це досягає того ж результату, що і @Mark у своїй відповіді.

$(window).resize( $.debounce( 250, onResize) );

1
Тут також корисний Лодаш, який також має методи _.throttle та _.debounce. Я думаю, що дебютація - це чудовий підхід порівняно з вищеприйнятим прикладом.
Кевін Лірі

1
Так, ця відповідь була написана понад 5 років тому. З днів плагінів jQuery багато що трапилося. Ось окрема функція дебютації
jessegavin

26

Існує елегантне рішення за допомогою Underscore.js Отже, якщо ви використовуєте його у своєму проекті, ви можете зробити наступне -

$( window ).resize( _.debounce( resizedw, 500 ) );

Цього має бути достатньо :) Але, якщо вам цікаво прочитати більше про це, ви можете перевірити мій пост у блозі - http://rifatnabi.com/post/detect-end-of-jquery-resize-event-using-underscore -відступ (мертве посилання)


1
Просто хочу додати, що lodashтакож забезпечують це
vsync

10

Одне рішення - це розширити jQuery з функцією, наприклад: resized

$.fn.resized = function (callback, timeout) {
    $(this).resize(function () {
        var $this = $(this);
        if ($this.data('resizeTimeout')) {
            clearTimeout($this.data('resizeTimeout'));
        }
        $this.data('resizeTimeout', setTimeout(callback, timeout));
    });
};

Використання зразка:

$(window).resized(myHandler, 300);


7

Ви можете зберігати ідентифікатор посилання на будь-який набірInterval або setTimeout. Подобається це:

var loop = setInterval(func, 30);

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

Для цього без "глобальної" змінної ви можете додати локальну змінну до самої функції. Наприклад:

$(window).resize(function() {
    clearTimeout(this.id);
    this.id = setTimeout(doneResizing, 500);
});

function doneResizing(){
  $("body").append("<br/>done!");   
}

4

Ви можете використовувати 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));
});

Оновлення

Я написав розширення, щоб поліпшити jQuery за замовчуванням on(& bind) -event-handler. Він додає функцію обробника подій для однієї або декількох подій до вибраних елементів, якщо подія не була запущена протягом заданого інтервалу. Це корисно, якщо ви хочете запустити зворотний дзвінок лише після затримки, як-от подія зміни розміру чи інше. 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/


3

Існує набагато простіший метод виконання функції в кінці розміру, ніж обчислення часу дельти між двома викликами, просто зробіть це так:

var resizeId;
$(window).resize(function() {
    clearTimeout(resizeId);
    resizeId = setTimeout(resizedEnded, 500);
});

function resizedEnded(){
    ...
}

І еквівалент для Angular2 :

private resizeId;
@HostListener('window:resize', ['$event'])
onResized(event: Event) {
  clearTimeout(this.resizeId);
  this.resizeId = setTimeout(() => {
    // Your callback method here.
  }, 500);
}

Для кутового методу використовуйте () => { }позначення в програмі setTimeoutдля збереження області, інакше ви не зможете робити жодних викликів функцій або використовувати this.


2

Це модифікація коду Долана вище, я додав функцію, яка перевіряє розмір вікна на початку зміни розміру і порівнює його з розміром в кінці розміру, якщо розмір більший або менший за маржу ( напр., 1000) потім він перезавантажується.

var rtime = new Date(1, 1, 2000, 12,00,00);
var timeout = false;
var delta = 200;
var windowsize = $window.width();
var windowsizeInitial = $window.width();

$(window).on('resize',function() {
    windowsize = $window.width();
    rtime = new Date();
    if (timeout === false) {
            timeout = true;
            setTimeout(resizeend, delta);
        }
});

function resizeend() {
if (new Date() - rtime < delta) {
    setTimeout(resizeend, delta);
    return false;
} else {
        if (windowsizeInitial > 1000 && windowsize > 1000 ) {
            setTimeout(resizeend, delta);
            return false;
        }
        if (windowsizeInitial < 1001 && windowsize < 1001 ) {
            setTimeout(resizeend, delta);
            return false;
        } else {
            timeout = false;
            location.reload();
        }
    }
    windowsizeInitial = $window.width();
    return false;
}

2

Відповідь Марка Коулмана, безумовно, набагато краща, ніж обрана відповідь, але якщо ви хочете уникнути глобальної змінної для ідентифікатора тайм-ауту ( doitзмінна у відповіді Марка), ви можете зробити одне з наступного:

(1) Використовуйте вираз функції, що негайно викликається (IIFE), щоб створити закриття.

$(window).resize((function() { // This function is immediately invoked
                               // and returns the closure function.
    var timeoutId;
    return function() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() {
            timeoutId = null; // You could leave this line out.
            // Code to execute on resize goes here.
        }, 100);
    };
})());

(2) Використовуйте властивість функції обробника подій.

$(window).resize(function() {
    var thisFunction = arguments.callee;
    clearTimeout(thisFunction.timeoutId);
    thisFunction.timeoutId = setTimeout(function() {
        thisFunction.timeoutId = null; // You could leave this line out.
        // Code to execute on resize goes here.
    }, 100);
});

Варіант 2, використовуючи argument.callee, не працюватиме, якщо функція буде перекладена з ES6.
Мартін Берч

1

Я написав функцію обгортки для нігтів самостійно ...

onResize  =   function(fn) {
    if(!fn || typeof fn != 'function')
        return 0;

    var args    = Array.prototype.slice.call(arguments, 1);

    onResize.fnArr    = onResize.fnArr || [];
    onResize.fnArr.push([fn, args]);

    onResize.loop   = function() {
        $.each(onResize.fnArr, function(index, fnWithArgs) {
            fnWithArgs[0].apply(undefined, fnWithArgs[1]);
        });
    };

    $(window).on('resize', function(e) {
        window.clearTimeout(onResize.timeout);
        onResize.timeout    = window.setTimeout("onResize.loop();", 300);
    });
};

Ось використання:

var testFn  = function(arg1, arg2) {
    console.log('[testFn] arg1: '+arg1);
    console.log('[testFn] arg2: '+arg2);
};

// document ready
$(function() {
    onResize(testFn, 'argument1', 'argument2');
});

1
(function(){
    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1);

    special.resizestart = {
        setup: function() {
            var timer,
                handler =  function(evt) {
                    var _self = this,
                        _args = arguments;
                    if (timer) {
                        clearTimeout(timer);
                    } else {
                        evt.type = 'resizestart';
                        jQuery.event.handle.apply(_self, _args);
                    }

                    timer = setTimeout( function(){
                        timer = null;
                    }, special.resizestop.latency);
                };
            jQuery(this).bind('resize', handler).data(uid1, handler);
        },
        teardown: function(){
            jQuery(this).unbind( 'resize', jQuery(this).data(uid1) );
        }
    };

    special.resizestop = {
        latency: 200,
        setup: function() {
            var timer,
                handler = function(evt) {
                    var _self = this,
                        _args = arguments;
                    if (timer) {
                        clearTimeout(timer);
                    }
                    timer = setTimeout( function(){
                        timer = null;
                        evt.type = 'resizestop';
                        jQuery.event.handle.apply(_self, _args);
                    }, special.resizestop.latency);
                };

            jQuery(this).bind('resize', handler).data(uid2, handler);
        },
        teardown: function() {
            jQuery(this).unbind( 'resize', jQuery(this).data(uid2) );
        }
    };
})();

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

1

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

Сказавши це, можливо, ви хочете встановити затримку вашого продовження? Ось приклад.

var t = -1;
function doResize()
{
    document.write('resize');
}
$(document).ready(function(){
    $(window).resize(function(){
        clearTimeout(t);
        t = setTimeout(doResize, 1000);
    });
});

1

Ось ДУЖЕ простий скрипт для запуску події "resizestart" і "resizeend" на об'єкті вікна.

Немає необхідності гнатися з датами та часом.

dЗмінна являє число мілісекунд між подіями зміни розміру перед запуском кінцевого зміни розміру події, ви можете грати з цим , щоб змінити , наскільки чутливими до кінця події.

Для прослуховування цих подій все, що вам потрібно зробити:

resizestart: $(window).on('resizestart', function(event){console.log('Resize Start!');});

resizeend: $(window).on('resizeend', function(event){console.log('Resize End!');});

(function ($) {
    var d = 250, t = null, e = null, h, r = false;

    h = function () {
        r = false;
        $(window).trigger('resizeend', e);
    };

    $(window).on('resize', function (event) {
        e = event || e;
        clearTimeout(t);

        if (!r) {
            $(window).trigger('resizestart', e);
            r = true;
        }

        t = setTimeout(h, d);
    });
}(jQuery));

1
Мені потрібні початок і кінець розміру, і це виглядає так, що він працює добре (перевірено в Chrome, FF, Opera та IE11). Для тестування я створив JSFiddle з вашим рішенням: jsfiddle.net/8fsn2joj
Keith DC

1

Це те, що я використовую для затримки повторних дій, його можна викликати в декількох місцях у вашому коді:

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

Використання:

$(window).resize(function () { 
   debounce(function() {
          //...
    }, 500);
});

0

оскільки обрана відповідь насправді не працює .. і якщо ви не використовуєте jquery, тут проста функція дросельної заслінки з прикладом того, як її використовувати із зміною розміру вікна

    function throttle(end,delta) {

    var base = this;

    base.wait = false;
    base.delta = 200;
    base.end = end;

    base.trigger = function(context) {

        //only allow if we aren't waiting for another event
        if ( !base.wait ) {

            //signal we already have a resize event
            base.wait = true;

            //if we are trying to resize and we 
            setTimeout(function() {

                //call the end function
                if(base.end) base.end.call(context);

                //reset the resize trigger
                base.wait = false;
            }, base.delta);
        }
    }
};

var windowResize = new throttle(function() {console.log('throttle resize');},200);

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

0

це працювало для мене, оскільки я не хотів використовувати жодні плагіни.

$(window).resize(function() {
    var originalWindowSize = 0;
    var currentWidth = 0;

    var setFn = function () {
        originalWindowSize = $(window).width();
    };

    var checkFn = function () {
        setTimeout(function () {
            currentWidth = $(window).width();
            if (currentWidth === originalWindowSize) {
                console.info("same? = yes") 
                // execute code 
            } else {
                console.info("same? = no"); 
                // do nothing 
            }
        }, 500)
    };
    setFn();
    checkFn();
});

У вікні повторного розміру викликайте "setFn", який отримує ширину вікна і зберігає як "originalWindowSize". Потім виберіть "checkFn", який через 500 мс (або ваш уподобання) отримує поточний розмір вікна, і порівнює оригінал із поточним, якщо вони не однакові, то вікно все ще змінюється за розміром. Не забудьте видалити консольні повідомлення у виробництві, і (необов’язково) можна зробити "setFn" самовиконанням.


0
var resizeTimer;
$( window ).resize(function() {
    if(resizeTimer){
        clearTimeout(resizeTimer);
    }
    resizeTimer = setTimeout(function() {
        //your code here
        resizeTimer = null;
        }, 200);
    });

Це працювало на те, що я намагався зробити в хромі. Це не призведе до зворотного виклику до 200 мс після останньої події зміни розміру.


0

ОНОВЛЕННЯ!

Краща альтернатива також створена мною тут: https://stackoverflow.com/a/23692008/2829600 (підтримує "видалити функції")

ОРИГІНАЛЬНА ПОСТ:

Я написав цю просту функцію для обробки затримки у виконанні, корисну всередині jQuery .scroll () та .resize (), тож callback_f буде працювати лише один раз для певного рядка id.

function delay_exec( id, wait_time, callback_f ){

    // IF WAIT TIME IS NOT ENTERED IN FUNCTION CALL,
    // SET IT TO DEFAULT VALUE: 0.5 SECOND
    if( typeof wait_time === "undefined" )
        wait_time = 500;

    // CREATE GLOBAL ARRAY(IF ITS NOT ALREADY CREATED)
    // WHERE WE STORE CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID
    if( typeof window['delay_exec'] === "undefined" )
        window['delay_exec'] = [];

    // RESET CURRENTLY RUNNING setTimeout() FUNCTION FOR THIS ID,
    // SO IN THAT WAY WE ARE SURE THAT callback_f WILL RUN ONLY ONE TIME
    // ( ON LATEST CALL ON delay_exec FUNCTION WITH SAME ID  )
    if( typeof window['delay_exec'][id] !== "undefined" )
        clearTimeout( window['delay_exec'][id] );

    // SET NEW TIMEOUT AND EXECUTE callback_f WHEN wait_time EXPIRES,
    // BUT ONLY IF THERE ISNT ANY MORE FUTURE CALLS ( IN wait_time PERIOD )
    // TO delay_exec FUNCTION WITH SAME ID AS CURRENT ONE
    window['delay_exec'][id] = setTimeout( callback_f , wait_time );
}


// USAGE

jQuery(window).resize(function() {

    delay_exec('test1', 1000, function(){
        console.log('1st call to delay "test1" successfully executed!');
    });

    delay_exec('test1', 1000, function(){
        console.log('2nd call to delay "test1" successfully executed!');
    });

    delay_exec('test1', 1000, function(){
        console.log('3rd call to delay "test1" successfully executed!');
    });

    delay_exec('test2', 1000, function(){
        console.log('1st call to delay "test2" successfully executed!');
    });

    delay_exec('test3', 1000, function(){
        console.log('1st call to delay "test3" successfully executed!');
    });

});

/* RESULT
3rd call to delay "test1" successfully executed!
1st call to delay "test2" successfully executed!
1st call to delay "test3" successfully executed!
*/

Чи можете ви пояснити використання тут? Ви пропонуєте один робить: $(window).resize(function() { delay_exec('test1', 30, function() { ... delayed stuff here ... }); });? Досить чистий код інакше. Дякую, що поділились. :)
mhulse

Ти рок! Дякую @ Déján! +1 весь шлях. Приклад класного коду, він дуже добре працює з того, що я перевірив. Простий у використанні також. Ще раз дякую за обмін. :)
mhulse

0

Події ResizeStart і ResizeEnd для вікна

http://jsfiddle.net/04fLy8t4/

Я реалізував функцію, яка запускає дві події на елементі DOM користувача:

  1. resizestart
  2. resizeend

Код:

var resizeEventsTrigger = (function () {
    function triggerResizeStart($el) {
        $el.trigger('resizestart');
        isStart = !isStart;
    }

    function triggerResizeEnd($el) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function () {
            $el.trigger('resizeend');
            isStart = !isStart;
        }, delay);
    }

    var isStart = true;
    var delay = 200;
    var timeoutId;

    return function ($el) {
        isStart ? triggerResizeStart($el) : triggerResizeEnd($el);
    };

})();

$("#my").on('resizestart', function () {
    console.log('resize start');
});
$("#my").on('resizeend', function () {
    console.log('resize end');
});

window.onresize = function () {
    resizeEventsTrigger( $("#my") );
};

0
var flag=true;
var timeloop;

$(window).resize(function(){
    rtime=new Date();
    if(flag){
        flag=false;
        timeloop=setInterval(function(){
            if(new Date()-rtime>100)
                myAction();
        },100);
    }
})
function myAction(){
    clearInterval(timeloop);
    flag=true;
    //any other code...
}

0

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

var tranStatus = false;
$(window).resizeend(200, function(){
    $(".cat-name, .category").removeAttr("style");
    //clearTimeout(homeResize);
    $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
      tranStatus = true;
    });
    processResize();
});

function processResize(){
  homeResize = setInterval(function(){
    if(tranStatus===false){
        console.log("not yet");
        $("*").one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(event) {
            tranStatus = true;
        }); 
    }else{
        text_height();
        clearInterval(homeResize);
    }
  },200);
}

0

Я написав функцію, яка передає функцію під час завершення будь-якої події зміни розміру. Він використовує інтервал, щоб розмір навіть не постійно створював події очікування. Це дозволяє йому виконувати незалежно від події зміни розміру, крім запису журналу, який слід видалити у виробництві.

https://github.com/UniWrighte/resizeOnEnd/blob/master/resizeOnEnd.js

        $(window).resize(function(){
            //call to resizeEnd function to execute function on resize end.
    //can be passed as function name or anonymous function
            resizeEnd(function(){



    });

        });

        //global variables for reference outside of interval
        var interval = null;
        var width = $(window).width();
    var numi = 0; //can be removed in production
        function resizeEnd(functionCall){
            //check for null interval
            if(!interval){
                //set to new interval
                interval = setInterval(function(){
        //get width to compare
                    width2 = $(window).width();
        //if stored width equals new width
                    if(width === width2){
                        //clear interval, set to null, and call passed function
                        clearInterval(interval);
                        interval = null; //precaution
                        functionCall();

                    }
        //set width to compare on next interval after half a second
                    width = $(window).width();
                }, 500);

            }else{
                //logging that should be removed in production
                console.log("function call " + numi++ + " and inteval set skipped");

            }

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.