JavaScript窗口调整大小事件

JavaScript window resize event

如何挂钩浏览器窗口调整大小事件?

有一种侦听调整大小事件的jQuery方式,但我不希望仅出于这一要求就将其引入我的项目中。


jQuery只是包装了标准的resize DOM事件,例如。

1
2
3
window.onresize = function(event) {
    ...
};

jQuery可以做一些工作来确保在所有浏览器中一致地触发resize事件,但是我不确定任何浏览器是否有所不同,但是我鼓励您在Firefox,Safari和IE中进行测试。


切勿覆盖window.onresize函数。

而是,创建一个将事件侦听器添加到对象或元素的函数。
这会检查并防止侦听器不起作用,然后作为最后手段重写该对象的功能。这是在jQuery之类的库中使用的首选方法。

object:元素或窗口对象
type:调整大小,滚动(事件类型)
callback:函数参考

1
2
3
4
5
6
7
8
9
10
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;
    }
};

然后使用是这样的:

1
addEvent(window,"resize", function_reference);

或具有匿名功能:

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


首先,我知道上面的注释中已经提到了addEventListener方法,但是我没有看到任何代码。由于这是首选方法,因此它是:

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

这是一个工作示例。


调整大小事件永远不要直接使用,因为它在调整大小时会连续触发。

使用去抖动功能来减轻过多的呼叫。

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

这是一种常见的在网上浮动的反跳,尽管确实会在lodash中寻找更高级的反跳。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
    };
};

可以这样使用...

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

每200ms发射一次就不会超过一次。

对于移动方向更改,请使用:

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

我整理了一个小图书馆来妥善处理这个问题。


我确实相信@Alex V已经提供了正确的答案,但是答案确实需要一些现代化,因为它已经超过5年了。

有两个主要问题:

  • 切勿使用object作为参数名称。这是保留字。话虽如此,@ Alex V提供的功能在strict mode中将不起作用。

  • 如果使用addEventListener方法,则@Alex V提供的addEvent函数不会返回event object。另一个参数应该添加到addEvent函数中以允许此操作。

  • 注意:addEvent的新参数已成为可选参数,因此,迁移到此新功能版本不会中断对该功能的任何先前调用。将支持所有传统用途。

    这是具有以下更改的更新的addEvent函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /*
        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函数的示例调用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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);


    2018年以上的解决方案:

    您应该使用ResizeObserver。与使用resize事件相比,这将是一种浏览器本地解决方案,其性能要好得多。此外,它不仅支持document上的事件,而且还支持任意elements上的事件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);

    目前,只有Chrome支持它,但其他浏览器很可能会很快支持。现在,您必须使用polyfill。


    感谢您在http://mbccs.blogspot.com/2007/11/fixing-window-resize-event-in-ie.html中引用我的博客文章。

    虽然您可以连接到标准窗口调整大小事件,但您会发现在IE中,该事件每X触发一次,每Y轴移动触发一次,从而导致触发大量事件,这些事件可能具有一定的性能如果渲染是一项繁重的任务,则会对您的网站产生影响。

    我的方法涉及一个短暂的超时,该超时将在后续事件中取消,以便在用户完成调整窗口大小之前,该事件不会冒泡到您的代码中。


    1
    2
    3
    window.onresize = function() {
        // your code
    };


    以下博客文章可能对您有用:在IE中修复窗口调整大小事件

    它提供了以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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);
    }


    如果您只想调整窗口和窗口的大小,则上面已经提到的解决方案将起作用。但是,如果您希望将调整大小传播到子元素,则需要自己传播事件。这是一些示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
    });

    请注意,子元素可以调用

    1
     event.preventDefault();

    在作为调整大小事件的第一个Arg传入的事件对象上。例如:

    1
    2
    3
    4
    5
    var child1 = document.getElementById("child1");
    child1.addEventListener("resize", function (event) {
      ...
      event.preventDefault();
    });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    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_];
            }
        }
    }


    1
    2
    3
    4
    <script language="javascript">
        window.onresize = function() {
        document.getElementById('ctl00_ContentPlaceHolder1_Accordion1').style.height = '100%';
    }