AngularJS event on window innerWidth size change

114,843

Solution 1

We could do it with jQuery:

$(window).resize(function(){
    alert(window.innerWidth);

    $scope.$apply(function(){
       //do something to update current scope based on the new innerWidth and let angular update the view.
    });
});

Be aware that when you bind an event handler inside scopes that could be recreated (like ng-repeat scopes, directive scopes,..), you should unbind your event handler when the scope is destroyed. If you don't do this, everytime when the scope is recreated (the controller is rerun), there will be 1 more handler added causing unexpected behavior and leaking.

In this case, you may need to identify your attached handler:

  $(window).on("resize.doResize", function (){
      alert(window.innerWidth);

      $scope.$apply(function(){
          //do something to update current scope based on the new innerWidth and let angular update the view.
      });
  });

  $scope.$on("$destroy",function (){
      $(window).off("resize.doResize"); //remove the handler added earlier
  });

In this example, I'm using event namespace from jQuery. You could do it differently according to your requirements.

Improvement: If your event handler takes a bit long time to process, to avoid the problem that the user may keep resizing the window, causing the event handlers to be run many times, we could consider throttling the function. If you use underscore, you can try:

$(window).on("resize.doResize", _.throttle(function (){
    alert(window.innerWidth);

    $scope.$apply(function(){
        //do something to update current scope based on the new innerWidth and let angular update the view.
    });
},100));

or debouncing the function:

$(window).on("resize.doResize", _.debounce(function (){
     alert(window.innerWidth);

     $scope.$apply(function(){
         //do something to update current scope based on the new innerWidth and let angular update the view.
     });
},100));

Difference Between throttling and debouncing a function

Solution 2

No need for jQuery! This simple snippet works fine for me. It uses angular.element() to bind window resize event.

/**
 * Window resize event handling
 */
angular.element($window).on('resize', function () {
    console.log($window.innerWidth);
});    

Unbind event

/**
 * Window resize unbind event      
 */
angular.element($window).off('resize');

Solution 3

I found a jfiddle that might help here: http://jsfiddle.net/jaredwilli/SfJ8c/

Ive refactored the code to make it simpler for this.

// In your controller
var w = angular.element($window);
$scope.$watch(
  function () {
    return $window.innerWidth;
  },
  function (value) {
    $scope.windowWidth = value;
  },
  true
);

w.bind('resize', function(){
  $scope.$apply();
});

You can then reference to windowWidth from the html

<span ng-bind="windowWidth"></span>

Solution 4

If Khanh TO's solution caused UI issues for you (like it did for me) try using $timeout to not update the attribute until it has been unchanged for 500ms.

var oldWidth = window.innerWidth;
$(window).on('resize.doResize', function () {
    var newWidth = window.innerWidth,
        updateStuffTimer;

    if (newWidth !== oldWidth) {
        $timeout.cancel(updateStuffTimer);
    }

    updateStuffTimer = $timeout(function() {
         updateStuff(newWidth); // Update the attribute based on window.innerWidth
    }, 500);
});

$scope.$on('$destroy',function (){
    $(window).off('resize.doResize'); // remove the handler added earlier
});

Reference: https://gist.github.com/tommaitland/7579618

Share:
114,843
Liad Livnat
Author by

Liad Livnat

Awesome client side / server side / mobile (HTML5) / facebook developer. Web Client: AngularJS, Backbone.js, Ember.js, Javascript(JQuery), HTML5, CSS3. Server: PHP - cake, CodeIgniter, Symfony, Laravel, Ruby , MVC 4.0, C# and ASP.NET 4.0 Database: MongoDB, mySQL, MSSQL ,Azure, Google Cloud : Azure, Amazon EC2, engineyard, Rackspace Source Controls: TFS, SVN, BitBucket, Git http://il.linkedin.com/in/liadl/ http://liadlivnat.com

Updated on March 25, 2020

Comments

  • Liad Livnat
    Liad Livnat about 4 years

    I'm looking for a way to watch changes on window inner width size change. I tried the following and it didn't work:

    $scope.$watch('window.innerWidth', function() {
         console.log(window.innerWidth);
    });
    

    any suggestions?

  • user1431317
    user1431317 almost 8 years
    The first two code snippets do not work properly. The watcher function is only triggered if something else starts a digest cycle, so those examples won't work on their own: jsfiddle.net/U3pVM/25385
  • Khanh TO
    Khanh TO almost 8 years
    @user1431317: ok, thanks very much for this catch, I'm not sure why I thought it was working more than 1 year ago (maybe on older browsers or on a different angular version).But I'll remove these solutions are they're not recommended anyway.
  • user1431317
    user1431317 almost 8 years
    You may have had something else triggering a digest cycle.
  • Rebecca
    Rebecca about 7 years
    Note, this may only work because there is a listener bound to the 'resize' event. You can't use $watch alone. $watch will only run and compare old & new values when something causes angular to trigger a $digest cycle. Window resizing does not trigger a $digest cycle on its own. So, as long as you have to bind to 'resize' anyway and all it's doing is triggering a $digest, you might as well just do the work in the 'resize' listener; this is totally duplicative to use both the event listener and the $watch listener.
  • Jon Onstott
    Jon Onstott about 7 years
    Remember to use angular.element($window).off(...) to deregister the handler, as needed. Angular doesn't seem to do this automatically when you go to a different Angular page.
  • Jon Onstott
    Jon Onstott about 7 years
    See the answer by lin which uses angular.element(). Shorter and simpler.
  • Khanh TO
    Khanh TO almost 7 years
    @Jon Onstott: that is another option, but I'd prefer using jQuery as it supports event namespaces. The reason for this is we need to un-register event handlers when the scope is destroyed, i don't want to accidentally un-register all event handlers (from other components). angular.element does not support event namespaces: docs.angularjs.org/api/ng/function/angular.element
  • Jon Onstott
    Jon Onstott almost 7 years
    @KhanhTO Ah I see, that makes sense
  • rgpass
    rgpass about 6 years
    @KhanhTO You made a change to your answer without giving credit. Thumbs down.