Coder Social home page Coder Social logo

javascript-detect-element-resize's Introduction

javascript-detect-element-resize

A Cross-Browser, Event-based, Element Resize Detection.

In short, this implementation does NOT use an internal timer to detect size changes (as most implementations I found do). It uses scroll events on most browsers, and the onresize event on IE10 and below.

The method used not only detects javascript generated resize changes but also changes made from CSS pseudo classes e.g. :hover, CSS animations, etc.

About the libraries

I was searching for a library that allowed me to detect when an DOM element changes size, and all solutions I found had two problems:

  1. only available as jQuery libraries (so no standalone Javascript)
  2. all had terrible performance (because all of them use timers to intermittently poll the size of the elements to detect a change).

Then I came across this great post on Back Alley Coder about using overflow and underflow events scroll events to do event-based element resize detection; and it works great without consuming resources at all (just like any other browser originated event).

The libraries on this repository are just a ready-to-use implementation of the above, one pure javascript and the other a jQuery plugin version (just for convenience).

Libraries

Pure Javascript library usage

<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>

jQuery plugin library usage

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.resize.js"></script>
<script type="text/javascript">
  var myFunc = function() {
    /* do something */
  };
  
  $('#resizeElement').resize(myFunc);
  $('#resizeElement').removeResize(myFunc);
</script>

Compatibility

Works great on:

  • Chrome
  • Firefox
  • IE 11 and below (tested on 11, 10, 9, 8 and 7)

Known Issues:

  • On IE 10 and below: If you detach the element and re-attach it, you will need to add the resize listener again.

Doesn't work on:

  • ???

Please let me know if you test these libraries on any other browser, of if you run into issues with any of the above browsers.

TODO

  • Fix detach/re-attach issue on IE 10 and below (IE 9 and below doesn't support CSS animations so we can use those as in the rest of the browsers).
  • Create minified version of the libraries.
  • Add support for standard jQuery bind method on 'resize' event.

Release Notes

v0.5.3

  • Fix for when the element is inside a display:none, and for when it is detached and reattached (changed @thomassuckow and @jerjou fixes to properly use CSS animations)
  • Adding /tests/ with some general QUnit tests to help test on multiple browsers

v0.5.2

  • Adding a bower.json file (thanks @adamjcook)
  • Fix style being appended to head multiple times (thanks @thomassuckow and @progman32)
  • Work around a chrome bug that would show scrollbars in some cases (thanks @thomassuckow)

v0.5.1

  • Fix for resize event on IE

v0.5

  • It is now fully compatible with IE11.
  • Rework of the libraries using the new scroll-event-based code of Back Alley Coder. For the pure javascript version I pretty much used the original code from Back Alley Coder and only had to add code to dynamically insert the styling for the resize-triggers.

v0.4.1

  • Fix for jQuery 'resize' method overlapping.

v0.4

  • Adds better cross-browser support, it now uses MutationObservers only on IE11.

v0.3

  • Adds support for MutationObservers.
  • Adds support for IE 11.
  • Wrapped the pure javascript version of the library (to hide non-public methods).

v0.2

  • Adds support for IE 8 and below.

v0.1

References

Similar libraries (but they use timers)

jQuery-mutate

jQuery-resize-plugin

Don't get me wrong, these are great libraries and work as advertised, it's just that they are not easy on browser resources.

External links

Back Alley Coder: Cross-Browser, Event-based, Element Resize Detection
Back Alley Coder: Overflow and Underflow Events

javascript-detect-element-resize's People

Contributors

sdecima avatar sverrejoh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

javascript-detect-element-resize's Issues

Moving an element or parent element to another part of the DOM breaks the listener.

I have used the library with good results :) On the site that I am developing though the elements get rearranged when the browser width is made smaller. This is achieved with jQuery append without detaching the DOM element which leaves for example click event listeners in place. This makes resizes of the element no longer fire any events. :(

Is there any work around or fix for this? Should I reinitialize the resize listener?

Reproduction:
http://jsfiddle.net/a3gmxmmk/2/

Browser: Windows 7 Chrome

Resize events when shrinking don't fire when the document has dir="rtl" set

Resize events when shrinking don't fire when the document has dir="rtl" set

My document has the attribute dir="rtl". When I make the browser larger resize events are fired as per normal. When I make the window smaller no events are fired.

I see this behaviour in Chrome Version 70.0.3538.77 and Firefox 63.0.1

Doesn't seem to trigger correctly in Chrome 47

Version 47.0.2526.80 (64-bit) OSX

This plugin seems to not trigger when the element reaches its final size. We have an transition that expands a panel with a chart inside, we use this make sure the chart is rendered appropriately (fill the width of the element). In the latest chrome it ends up stretched because it resizes to an intermediate value of the transition and not to the final size.

May be a chrome bug, though I am not sure what I would report/search for.

Use with a table blocks content on macOS Safari

I am setting a resize handler on a table element, and this works fine in Chrome, Firefox, and IE. However, on macOS Safari 10.1.1, the resize-triggers div blocks some of the table content. That is, you can see the content but you can't get click events to it.

I fixed this in my local copy of detect-element-resize by simply changing createStyles to specify z-index: -1 in the style block for resize-triggers etc. This fixed the problem, apparently without any negative consequences.

jQuery 'resize' event handler behaves differently in IE8 (possibly other vers.) than Firefox.

Hi,

This isn't a bug per se, but an inconsistency between browsers.

When handling the 'resize' event in non IE browsers to get the height of the element - the reason for using this library, I could write

var resizeNavArrow = function () {
    var anchor = $(this).find("a");
    var newButtonHeight = parseInt(anchor.innerHeight(), 10);
    .
    .
    .
}

In IE8, I noticed $(this) wasn't working and my newButtonHeight was NaN, so modified the function with the 'el' parameter. In testing, 'el' was an MSObject for an event - so I guess I can change it to 'evt'. I could access the element I wanted via srcElement.

var resizeNavArrow = function (el) {
    var anchor = null;
    if (utility.IsIEOlderOrEqualTo(8)) {
        anchor = $(el.srcElement).find("a");
    } else {
        anchor = $(this).find("a");
    }
    var newButtonHeight = parseInt(anchor.innerHeight(), 10);
    .
    .
    .
}

But back in Firefox, 'el' was 'scroll' - which I guess is the source element of the event you're handling - and 'srcElement' isn't a method of 'scroll' hence the IE logic.

So this is good as I can now support IE and non-IE browser. But it would be good if the element the 'resize' event handler was registered against was passed to the handler.

I have some

  • elements that are bound to the 'resize' event hanlder i.e.

    var navButtons = $(".nav-wizard li");
    navButtons.each(function (i) {
        if (i < navButtons.length - 1) {
            var li = $(this);
            li.resize(resizeNavArrow);
            .
            .
            .
    

    If I try binding to the event handler passing params, it simply calls the function during the binding
    e.g.
    li.resize(resizeNavArrow(li));

    Regards
    Mr A

  • Performance issues when nesting DIVs subscribed to element-resize

    Hello!

    We're facing performance "issues" while nesting several DIVs subscribed to element-resize. We tried having a container DIV with 9 inner DIVs. The main container is subscribed to element-resize (and changes its children's width dynamically), and the 9 inner DIVs are also subscribing to their element-resize, and perform other actions to their children.

    It takes lots of time of javascript processing (most of it in ResetTriggers()), and the frame rate drops to 1fps, 2fps... Have you encountered the same? Or have any solution or hint for that?

    Thank you!

    A method to support IE11 without having to rely on MutationObserver

    An idea I came up with for my own use:

    Create an <iframe src="about:blank"> and use absolute positioning to stretch it over the full dimensions of whatever element you want to observe for resizes. Clip it with clip:rect(0,0,0,0) to become 'invisible' to normal users.

    Populate said iframe with an empty HTML document that contains a simple script that defines a listener for the resize event and uses window.postMessage to notify the parent window of the resize event.

    The message posted by the iframe should encode a known, agreed upon ID from which the parent window can learn which actual element in its document tree had a resize happen. When the parent window receives the message, you can dispatch a local resize event from the element that was resized.

    Incase you were wondering: it's possible to synchronously populate the iframe and overcome cross-domain issues by using a simple little trick with the javascript: protocol. Set a custom property on the iframe's contentWindow that contains whatever markup you want to load into the frame. Then switch the iframe's src like so:

    iframe.contentWindow.emitcontent = "<!DOCTYPE html><html><body><script>(...)";
    iframe.src = "javascript:window.emitcontent";

    Please note: to hamper outside tampering, it is best to choose a randomly generated name. The emitcontent property is purely for illustration purposes.

    This technique has a hidden bonus as well: it will also result in a resize event whenever the element switches between visible and invisible status. That may be something of interest to a few people. (Quite a few, I can imagine, infact...)

    sometimes it doesn't work.

    I'm using the jquery version, and it works fine. However the functionality is intermittent.

    I'm using this on a wordpress website in combination with the "Collapse O-matic" plugin which expands and collapses parts of the content, stretching the container accordingly. My function wants to stretch the sidebar as well.

    What happens is that, as I expand and collapse the content area, the sidebar only grows and shrinks the first time, then it doesn't seem to respond any longer.

    This is how I call the function in the footer of the page:

      jQuery(function($) {
    	  
    		var myFunc = function() {
    		(((my function here)))
    		};		
    		$('#primary').resize(myFunc); 
    		/*$('#primary').removeResize(myFunc); if I don't comment out this line it doesn't work altogether*/
    
      });
    

    Here's the function I trigger, which is aimed at making the sidebar as long as the primary container:

    var sidebar = $('.sidebar');
    var contentArea = $('#primary');
    
    if (parseInt(sidebar.outerHeight(true)) > parseInt(contentArea.outerHeight(true))) {
    
    	sidebar.css('height',''); 
    
    } else {
    
    	sidebar.css('height',contentArea.outerHeight(true));
    }
    

    And this is what happens as I load the page:

    jQuery(function($) {	
    	$(window).on('resize load', function() {
    		(((my function here)))
    	});
    });
    

    contract-trigger css size and drag-drop plugin interference.

    I'm trying to figure out why the css of the contract-trigger is set as:

    .contract-trigger:before { width: 200%; height: 200%; }

    The issue that I have is that when combining with the plugin http://farhadi.ir/projects/html5sortable it causes the div being dragged to the 2x size.

    Would the resizing trigger miss firing if the css for before is set to <= 100%?

    I tested on Linux chrome and it triggered with 50%, but I don't have access to all the other browsers right now.

    Thanks,

    Alberto

    All ResizeListeners freeze on IE 9/10/11 when IE force scrollbars

    Hi, first of all awesome work with this, your detect element resize has been very helpful to me.
    I was tinkering and cross-browser testing it when I found the following unexpected behaviour:

    When you add a listener to a div (parent) with 100% height and width, and add a div to it (child) with a fixed px size, and resize the browser to a smaller size than the child, IE(9/10/11) forces scrollbars to be shown. The moment the scrollbars appear all resize event listener in this and all other divs you might have immediately stop working without reporting any error to the console.

    I hot fixed the issue by adding overflow: hidden to the parents, but I'm sure you can think of a better way of ensuring proper behaviour browser wise modifying your scrollbar event logic.

    Hope this helps to further improve your outstanding job, PM if you need help reproducing the issue or more info about it.

    Using the jquery plugin's removeResize function on "$(window)" throws an exception.

    I have some code that calls
    $(container).resize(myFunc);
    and then later
    $(container).removeResize(myFunc);

    Sometimes the container is an element, other times it is the window object. The resize function has check for when this == window and forwards to the default jquery resize handler. There is no such check in the removeResize function so it goes about trying to splice __resizeListeners__ which don't exist and throws an exception.

    Uncaught TypeError: Cannot read property 'splice' of undefined

    this refers to window object in IE10 and lower

    say for eg. I'm declaring resize function on an element

    $(".someclass li").resize(function*() {
         $(this).something something.......
    }
    

    the var this is the element that is being resized in IE11 and other modern browsers..but in IE10 and lower this is always the global window object.

    Once in a while I get an Uncaught TypeError

    Uncaught TypeError: Cannot read property 'firstElementChild' of undefined Slet
    TypeError: Cannot read property 'firstElementChild' of undefined
    at i (https://xxx/js/jquery/sdecima/jquery.resize.min-4.js:1:58)
    at HTMLDivElement.r (https://xxx/js/jquery/sdecima/jquery.resize.min-4.js:1:425)
    

    Duplicate style elements creation

    For each resize listener I bind to an HTML element, I get a new style element inserted into the document head. This looks to be due to not setting the 'stylesCreated' flag when the style element is created.

    Does not work properly under OSX Chrome / in general

    I've installed via Bower, which drops in the /javascript-detect-element-resize/detect-element-resize.js file.

    If I have the following in the js:

    ( function( $ ) {
        var do_something = function(){
          console.log('test');
        }
    
       $(window).resize(do_something);
    } )( jQuery );
    
    

    It seems to fire as if I had a regular resize event on the window.

    Adding:

    $(window).removeResize(do_something);
    

    ...throws a "Uncaught TypeError: undefined is not a function". And if I point it to an element it simply does not fire. The script is loading fine, so I'm confused.

    What am I missing?

    Overflow auto sizing bug on Safari

    One of the CSS rules on line 113 (.resize-triggers > div { overflow: auto; }) causes Safari to set overflow on contract-trigger and expand-trigger to auto, obviously. When used with a scrollable div, Safari gives these two divs a smaller width (because of the overflow: auto generates space for an invisible scrollbar) This causes the vertical scrollbar on the div to be un-clickable.

    As a fix, I simply removed the overflow: auto rule from the plugin, but am now obviously worried about unknown side-effects. What was the original purpose of this rule?

    I'll try to attach a jsbin or something ASAP.

    It does not work in iOS simulator (Cordova app), should it?

    Testing this library in a Cordova HTML5 application running on iOS simulator. Is it supposed to work?

    Basically I scroll up and down the application/HTML with the mouse, and have also tried changing CSS properties of the target element via the JavaScript console. It does not fire the resize listener at all.

    Resize By Iframe

    Hi,

    So i saw issue A method to support IE11 without having to rely on MutationObserver #2 and came up with idea of how to catch resize event, so what are your thoughts.

    (function($) {
        var stylesCreated = false;
        var createStyles = function() {
            var style = $('style');
            style.text('.jq-custom-frame-resize{position:absolute;top:0;left:0;width:100%;height:100%;border:0;z-index:-1;background:transparent;}');
            $('head').append(style);
        };
    
        $.fn.elementResize = function(fn) {
            if (stylesCreated === false) {
                createStyles();
            }
    
            var onFrameResize = function() { window.onresize = document.resizeCallback; };
    
            var frame = document.createElement('iframe');
            frame.setAttribute('src', 'javascript:;');
            frame.className = 'jq-custom-frame-resize';
            this.append(frame);
    
            var script = frame.contentWindow.document.createElement('script');
            script.textContent = ['(', onFrameResize.toString(), ')();'].join('');
            frame.contentWindow.document.resizeCallback = fn;
    
            var style = document.createElement('style');
            style.type = 'text/css';
            style.textContent = 'body,head{height:100%} body{margin:0;padding:0;}';
    
            var head = frame.contentWindow.document.querySelector('head');
            head.appendChild(script);
            head.appendChild(style);
        };
    })(jQuery);

    it looks simpler it works so... why is your way better?

    Safari 8 on Mac OS works fine, but with a minor behavior difference

    All browsers, except latest Safari on Mac OS X Yosemite, invokes the listener once initially, right after adding it, and before any actual resizing.

    In desktop Safari 8, the listener isn't invoked before the first resize. It appears that the scroll listener is not fired the first time in Safari.

    Not difficult to fix/workaround this outside the library, but a consistent behavior would be useful.

    Publish to npm

    This repository already has package.json and supports bower. Should be a light-to-fix issue (npm publish)

    event target not returning correct index value in IE 10 and less

    Hi,

    I'm trying to get the index of the element (relative to its parent) that is being resized like below
    $('.myoptions li').resize(function(e) {
    var changedRow = $(this).index(),
    newHeight = $(this).height();
    $('.dynamic-row-height'+changedRow).height(newHeight);
    })

    This is working gr8 in chrome, FF& IE11. But in IE 10 and less the changedRow is always retured as -1 instead of the actual index value.

    Any help would be much appreciated.

    Thanks,

    Improper use of scrollLeft and scrollRight in resetTriggers in non-default writing modes

    cc @cathiechen

    The resetTriggers function sets the scrollLeft/scrollTop properties of the expand/contract children of the resize-triggers div to a non-negative value.

    However, this is incorrect in some non-default writing modes where scrollLeft/scrollTop must be non-positive. See https://people.igalia.com/fwang/scrollable-elements-in-non-default-writing-modes/ ; Chrome is going to change to use the standard behavior https://www.chromestatus.com/feature/5759578031521792

    I'm not really sure how this polyfill for ResizeObserver is working, but a possible fix would be to force "direction: ltr; writing-mode: horizontal-tb;" on the expand/contract div. Or check the actual CSS direction/writing-mode to determine whether to use positive/negative values.

    why doesn't this work?

    when I use 'html' or 'body" and resize the browser sliding left to right or right to left, nothing happens.

    <!-- window resize detection event -->
    <script type="text/javascript">
        var topnavbar = <?php echo $topnavbar; ?>;
        var myFunc = function(jQuery) {
        jQuery('body').css('border', '44px solid blue'); 
      };
    
      jQuery('html').resize(myFunc);
      //jQuery('body').removeResize(myFunc);
    </script>

    what am I doing wrong?

    Browser History Issue

    Hi,

    Thanks for the plugin, which is really great.

    I've found one issue though that perhaps you can help with. In firefox the following line causes an entry to be added to the browser's history, which can cause problems with the back button -

    $iframe[0].src = 'javascript:window.emitcontent';

    I've found some comments to suggest that using $iframe[0].contentWindow.location.replace instead will not update the history, but without a full understanding of the code I'm not sure whether this will fix the problem and avoid introducing other issues.

    Perhaps you could let me know your thoughts when you can.

    Many thanks.

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.