8

So Chrome apparently has a bug that observing the size of an element with a MutationObserver doesn't work when the element is manually resized.

What should I use instead or in addition to that to make this work in Chrome, too, until the bug is fixed? Should I use the deprecated mutation events or is there another better alternative?

Is there a way to do this nice in AngularJS without using the element object?

Community
  • 1
  • 1
Mouagip
  • 5,139
  • 3
  • 37
  • 52

4 Answers4

3

In order to watch an element's size, you have the following options:

  • Using a MutationObserver you can watch the element's attributes that change when the element is manually resized. This is a bug in Chrome.

  • You can continually poll the element size with JavaScript in a timer interval, but you've indicated that this is not something you wish to use.

  • Your last option that I am aware of is to use CSS element queries.

The last option works by using the fact that all browsers support overflow events for elements. It will alter the layout of your existing page, forcing the element that you are rezing to be positioned relatively or absolutely. No problem at all if you've got a simple page with simple css, but more complicated layouts may run into issues.

Johnny5
  • 6,664
  • 3
  • 45
  • 78
Noishe
  • 1,411
  • 11
  • 14
  • Doing it with Angular is just optional. My main question is how to do it without the MutationObserver and maybe without mutation events if possible. There has to be a better way than using timeouts. – Mouagip Dec 22 '13 at 13:59
  • Alright, no angular then. Modified my answer with a link to http://marcj.github.io/css-element-queries/ which I found. – Noishe Dec 22 '13 at 23:33
  • I tend to believe that among all answers here using css element queries is the only efficient alternative to `MutationObserver`, thank you for that. – Mouagip Dec 28 '13 at 08:42
2

I ran into a similar issue while making a few responsive elements so I just ended up using window.onresize:

window.onresize = function(){
        //do element changes here
        $scope.$apply();
    };

not sure if that's what you're looking for but this approach worked well for me.

ruedamanuel
  • 1,930
  • 1
  • 22
  • 23
  • That would be a solution if you wanted to watch the window size. But I have to watch the size of an element that can be resized independently from the window. – Mouagip Dec 24 '13 at 09:59
1

Use watcher on scope in Angular

$scope.$watch(function() {
  // whatever element you want to watch
  return $element.height();
}, function(newVal, oldVal) {
  // do something
});

My best bid is that when you resizing any element, is likely to be done within another digest cycle if you use angular fully on your page. To fix that you could manually trigger digestion with $scope.$apply() when you manually resize the element or window.

// example to manual trigger digest with $apply()
app.run(function($window, $rootScope) {
  angular.element($window).on('resize', function() {
    $rootScope.$apply();
  });
});

This is better than setTimeout, because it does waste a lot function calls to retrieve the height. It only retrieve when there a possible change of a state. Also this is also more reliable than MutationObserver and MutationEvents when used across browser. What's more, I don't believe MutationObserver and MutationEvents will monitor the dimension of an element.

Daiwei
  • 40,666
  • 3
  • 38
  • 48
  • Does this also work if I used `$element[0].clientHeight` or do I run into cross-browser issues then? It only has to run in the newer Chrome/Firefox. – Mouagip Dec 24 '13 at 10:04
  • @Mouagip you can use clientHeight, there isn't any issue I'm aware. – Daiwei Dec 24 '13 at 15:41
  • As said [here](http://stackoverflow.com/a/20757139/1796523) the problem with this approach would be, that no digest is triggered by manually resizing the element. – Mouagip Dec 27 '13 at 15:31
  • @Mouagip my best bid is that when you resizing any element, is likely to be done within another digest cycle if you use angular fully on your page. To fix that you could manually trigger digestion with `$scope.$apply()` when you manually resize the element or window. – Daiwei Dec 27 '13 at 16:09
  • I can tell you that `MutationObserver` and mutation events *can* monitor the dimension of an element in this case of manually resizing it. I think `MutationObserver` would be the best solution of all others presented here if it weren't for the bug in Chrome... – Mouagip Dec 28 '13 at 08:36
1

My recommendation,

First, set a directive for the element you want to watch the height

Second, in the directive add a empty watcher as said at doc like this

If you want to be notified whenever $digest is called, 
you can register a watchExpression function with no listener.(Since 
watchExpression can execute multiple times per $digest cycle when a 
change is detected, be prepared for multiple calls to your listener.)

directive

scope.$watch( function(){
  scope.elHeight = element.height(); // or element.offsetHeight;
};

Third, set a watcher in controller for this height

cotroller

$scope.$watch('elHeight',  function(newVal, oldVal){
  console.log(newVal, oldVal)
};

Every single $digest means every single Angular-related changes within the scope.

--- edit ---
This covers most of angular-js related events. However it does not cover manual resizing, i.e. window resizing, vanilla javascript resizing, etc.

If I use AngularJS, I would fully use Angular all over the page. Thus,
for that case, I would fire small event to get $digest is called.

allenhwkim
  • 27,270
  • 18
  • 89
  • 122
  • This would work if I could get the height of the element without using jQuery (see my [comments here](http://stackoverflow.com/a/20750641/1796523)). – Mouagip Dec 24 '13 at 10:06
  • a digest won't be triggered when you manually resize the element – Noishe Dec 24 '13 at 15:29
  • with no jquery, I would use element[0].offsetHeight. and for manual resize, I would fire events using $element.on(...) – allenhwkim Dec 24 '13 at 15:48
  • `$element.on()` would use the deprecated mutation events. But maybe I use that until the bug in Chrome is fixed. – Mouagip Dec 27 '13 at 15:25
  • @allenhwkim Which events would you use? – Mouagip Dec 27 '13 at 15:38
  • i.e `$window.onresize = function() { // tell angular};`. example, http://jsfiddle.net/spacm/HeMZP/. Also to any element resizing code, tell angular do something about it. If `$element.on(..)` does not affect resizing, it could be an bad example. – allenhwkim Dec 27 '13 at 15:47