Skip to content

Commit

Permalink
Fix possible conflict between multiple observers
Browse files Browse the repository at this point in the history
  • Loading branch information
que-etc committed Feb 17, 2017
1 parent 3ee76bc commit 7d5c45b
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 123 deletions.
1 change: 0 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
"no-proto": "error",
"no-return-assign": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
Expand Down
56 changes: 28 additions & 28 deletions dist/ResizeObserver.es.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use strict';

/**
* Exports global object for the current environment.
*/
Expand Down Expand Up @@ -225,6 +223,12 @@ var throttle = function (callback, delay, afterRAF) {
return proxy;
};

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before the next iteration of the continuous cycle.
var CONTINUOUS_DELAY = 80;

// Define whether the MutationObserver is supported.
// eslint-disable-next-line no-extra-parens
var mutationsSupported = typeof MutationObserver === 'function' &&
Expand All @@ -234,12 +238,6 @@ var mutationsSupported = typeof MutationObserver === 'function' &&
// userAgent's information.
typeof navigator === 'object' && !(navigator.appName === 'Netscape' && navigator.userAgent.match(/Trident\/.*rv:11/));

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before iteration of the continuous cycle.
var CONTINUOUS_HANDLER_DELAY = 80;

/**
* Controller class which handles updates of ResizeObserver instances.
* It decides when and for how long it's necessary to run updates by listening
Expand All @@ -252,7 +250,7 @@ var CONTINUOUS_HANDLER_DELAY = 80;
* Continuous update cycle will be used automatically in case MutationObserver
* is not supported.
*/
var ResizeObserverController = function () {
var ResizeObserverController = function() {
/**
* Continuous updates must be enabled if MutationObserver is not supported.
*
Expand All @@ -267,6 +265,13 @@ var ResizeObserverController = function () {
*/
this.listenersEnabled_ = false;

/**
* Keeps reference to the instance of MutationObserver.
*
* @private {MutationObserver}
*/
this.mutationsObserver_ = null;

/**
* A list of connected observers.
*
Expand All @@ -279,7 +284,7 @@ var ResizeObserverController = function () {
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY, true);

// Additionally postpone invocation of the continuous updates.
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_HANDLER_DELAY);
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_DELAY);
};

/**
Expand Down Expand Up @@ -358,24 +363,19 @@ ResizeObserverController.prototype.refresh = function () {
* dimensions of it's elements.
*/
ResizeObserverController.prototype.updateObservers_ = function () {
var hasChanges = false;

for (var i = 0, list = this.observers_; i < list.length; i += 1) {
// Collect active observations.
var observer = list[i];

observer.gatherActive();

// Broadcast active observations and set the flag that changes have
// been detected.
if (observer.hasActive()) {
hasChanges = true;
// Collect observers that have active entries.
var active = this.observers_.filter(function (observer) {
return observer.gatherActive(), observer.hasActive();
});

observer.broadcastActive();
}
}
// Deliver notifications in a separate cycle in order to avoid any
// collisions between observers. E.g. when multiple instances of
// ResizeObserer are tracking the same element and the callback of one
// of them changes content dimensions of the observed target. Sometimes
// this may result in notifications being blocked for the rest of observers.
active.forEach(function (observer) { return observer.broadcastActive(); });

return hasChanges;
return active.length > 0;
};

/**
Expand Down Expand Up @@ -901,7 +901,7 @@ ResizeObserverSPI.prototype.unobserve = function (target) {

// Set back the initial state if there is nothing to observe.
if (!targets.size) {
this.disconnect();
this.controller_.disconnect(this);
}
};

Expand Down Expand Up @@ -974,7 +974,7 @@ ResizeObserverSPI.prototype.clearActive = function () {
* @returns {boolean}
*/
ResizeObserverSPI.prototype.hasActive = function () {
return !!this.activeTargets_.length;
return this.activeTargets_.length > 0;
};

// Controller that will be assigned to all instances of the ResizeObserver.
Expand Down
54 changes: 28 additions & 26 deletions dist/ResizeObserver.global.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ var throttle = function (callback, delay, afterRAF) {
return proxy;
};

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before the next iteration of the continuous cycle.
var CONTINUOUS_DELAY = 80;

// Define whether the MutationObserver is supported.
// eslint-disable-next-line no-extra-parens
var mutationsSupported = typeof MutationObserver === 'function' &&
Expand All @@ -239,12 +245,6 @@ var mutationsSupported = typeof MutationObserver === 'function' &&
// userAgent's information.
typeof navigator === 'object' && !(navigator.appName === 'Netscape' && navigator.userAgent.match(/Trident\/.*rv:11/));

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before iteration of the continuous cycle.
var CONTINUOUS_HANDLER_DELAY = 80;

/**
* Controller class which handles updates of ResizeObserver instances.
* It decides when and for how long it's necessary to run updates by listening
Expand All @@ -257,7 +257,7 @@ var CONTINUOUS_HANDLER_DELAY = 80;
* Continuous update cycle will be used automatically in case MutationObserver
* is not supported.
*/
var ResizeObserverController = function () {
var ResizeObserverController = function() {
/**
* Continuous updates must be enabled if MutationObserver is not supported.
*
Expand All @@ -272,6 +272,13 @@ var ResizeObserverController = function () {
*/
this.listenersEnabled_ = false;

/**
* Keeps reference to the instance of MutationObserver.
*
* @private {MutationObserver}
*/
this.mutationsObserver_ = null;

/**
* A list of connected observers.
*
Expand All @@ -284,7 +291,7 @@ var ResizeObserverController = function () {
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY, true);

// Additionally postpone invocation of the continuous updates.
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_HANDLER_DELAY);
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_DELAY);
};

/**
Expand Down Expand Up @@ -363,24 +370,19 @@ ResizeObserverController.prototype.refresh = function () {
* dimensions of it's elements.
*/
ResizeObserverController.prototype.updateObservers_ = function () {
var hasChanges = false;

for (var i = 0, list = this.observers_; i < list.length; i += 1) {
// Collect active observations.
var observer = list[i];

observer.gatherActive();

// Broadcast active observations and set the flag that changes have
// been detected.
if (observer.hasActive()) {
hasChanges = true;
// Collect observers that have active entries.
var active = this.observers_.filter(function (observer) {
return observer.gatherActive(), observer.hasActive();
});

observer.broadcastActive();
}
}
// Deliver notifications in a separate cycle in order to avoid any
// collisions between observers. E.g. when multiple instances of
// ResizeObserer are tracking the same element and the callback of one
// of them changes content dimensions of the observed target. Sometimes
// this may result in notifications being blocked for the rest of observers.
active.forEach(function (observer) { return observer.broadcastActive(); });

return hasChanges;
return active.length > 0;
};

/**
Expand Down Expand Up @@ -906,7 +908,7 @@ ResizeObserverSPI.prototype.unobserve = function (target) {

// Set back the initial state if there is nothing to observe.
if (!targets.size) {
this.disconnect();
this.controller_.disconnect(this);
}
};

Expand Down Expand Up @@ -979,7 +981,7 @@ ResizeObserverSPI.prototype.clearActive = function () {
* @returns {boolean}
*/
ResizeObserverSPI.prototype.hasActive = function () {
return !!this.activeTargets_.length;
return this.activeTargets_.length > 0;
};

// Controller that will be assigned to all instances of the ResizeObserver.
Expand Down
54 changes: 28 additions & 26 deletions dist/ResizeObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ var throttle = function (callback, delay, afterRAF) {
return proxy;
};

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before the next iteration of the continuous cycle.
var CONTINUOUS_DELAY = 80;

// Define whether the MutationObserver is supported.
// eslint-disable-next-line no-extra-parens
var mutationsSupported = typeof MutationObserver === 'function' &&
Expand All @@ -239,12 +245,6 @@ var mutationsSupported = typeof MutationObserver === 'function' &&
// userAgent's information.
typeof navigator === 'object' && !(navigator.appName === 'Netscape' && navigator.userAgent.match(/Trident\/.*rv:11/));

// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;

// Delay before iteration of the continuous cycle.
var CONTINUOUS_HANDLER_DELAY = 80;

/**
* Controller class which handles updates of ResizeObserver instances.
* It decides when and for how long it's necessary to run updates by listening
Expand All @@ -257,7 +257,7 @@ var CONTINUOUS_HANDLER_DELAY = 80;
* Continuous update cycle will be used automatically in case MutationObserver
* is not supported.
*/
var ResizeObserverController = function () {
var ResizeObserverController = function() {
/**
* Continuous updates must be enabled if MutationObserver is not supported.
*
Expand All @@ -272,6 +272,13 @@ var ResizeObserverController = function () {
*/
this.listenersEnabled_ = false;

/**
* Keeps reference to the instance of MutationObserver.
*
* @private {MutationObserver}
*/
this.mutationsObserver_ = null;

/**
* A list of connected observers.
*
Expand All @@ -284,7 +291,7 @@ var ResizeObserverController = function () {
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY, true);

// Additionally postpone invocation of the continuous updates.
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_HANDLER_DELAY);
this.continuousUpdateHandler_ = throttle(this.refresh, CONTINUOUS_DELAY);
};

/**
Expand Down Expand Up @@ -363,24 +370,19 @@ ResizeObserverController.prototype.refresh = function () {
* dimensions of it's elements.
*/
ResizeObserverController.prototype.updateObservers_ = function () {
var hasChanges = false;

for (var i = 0, list = this.observers_; i < list.length; i += 1) {
// Collect active observations.
var observer = list[i];

observer.gatherActive();

// Broadcast active observations and set the flag that changes have
// been detected.
if (observer.hasActive()) {
hasChanges = true;
// Collect observers that have active entries.
var active = this.observers_.filter(function (observer) {
return observer.gatherActive(), observer.hasActive();
});

observer.broadcastActive();
}
}
// Deliver notifications in a separate cycle in order to avoid any
// collisions between observers. E.g. when multiple instances of
// ResizeObserer are tracking the same element and the callback of one
// of them changes content dimensions of the observed target. Sometimes
// this may result in notifications being blocked for the rest of observers.
active.forEach(function (observer) { return observer.broadcastActive(); });

return hasChanges;
return active.length > 0;
};

/**
Expand Down Expand Up @@ -906,7 +908,7 @@ ResizeObserverSPI.prototype.unobserve = function (target) {

// Set back the initial state if there is nothing to observe.
if (!targets.size) {
this.disconnect();
this.controller_.disconnect(this);
}
};

Expand Down Expand Up @@ -979,7 +981,7 @@ ResizeObserverSPI.prototype.clearActive = function () {
* @returns {boolean}
*/
ResizeObserverSPI.prototype.hasActive = function () {
return !!this.activeTargets_.length;
return this.activeTargets_.length > 0;
};

// Controller that will be assigned to all instances of the ResizeObserver.
Expand Down
Loading

0 comments on commit 7d5c45b

Please sign in to comment.