Fix directive + add example
This commit is contained in:
@@ -1,128 +0,0 @@
|
|||||||
/**
|
|
||||||
* Created by Brice on 01/10/2014.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen to the scroll event and broadcast an event to disable / enable the watchers of the "dwhElements"
|
|
||||||
*/
|
|
||||||
webassadorsApp.directive('disableWhenHidden', ['$timeout', '$compile', 'CommonTools', function ($timeout, $compile, CommonTools) {
|
|
||||||
'use strict';
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
link: function (scope) {
|
|
||||||
var checkElements = CommonTools.debounce(function () {
|
|
||||||
scope.$broadcast('dwhCheckElements');
|
|
||||||
}, 250);
|
|
||||||
// Hide / Show posts every time the user scrolls
|
|
||||||
document.addEventListener('scroll', checkElements);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When out of sight, we empty the watchers, when back up we bring them back !
|
|
||||||
*/
|
|
||||||
webassadorsApp.directive('dwhElement', function () {
|
|
||||||
'use strict';
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
link: function (scope, element) {
|
|
||||||
var el = element[0];
|
|
||||||
var m = 1000; // Range of "errors" outside the viewport
|
|
||||||
var wArray; // Array to store all the watchers
|
|
||||||
var isHidden = false; // Used to prevent useless computation
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store all the watchers and empty it so we never go crazy
|
|
||||||
*/
|
|
||||||
var disableWatchers = function () {
|
|
||||||
// Hide element
|
|
||||||
wArray = [];
|
|
||||||
leaveHimToDie(el);
|
|
||||||
isHidden = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bring the watchers back
|
|
||||||
*/
|
|
||||||
var enableWatchers = function () {
|
|
||||||
bringHimBack(el);
|
|
||||||
isHidden = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Listen to scroll events
|
|
||||||
scope.$on('dwhCheckElements', function () {
|
|
||||||
var coordinates = el.getBoundingClientRect();
|
|
||||||
if (coordinates.bottom > 0 - m && coordinates.top < window.innerHeight + m) {
|
|
||||||
if (isHidden) {
|
|
||||||
enableWatchers();
|
|
||||||
}
|
|
||||||
} else if (!isHidden) {
|
|
||||||
disableWatchers();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Goes through every child elements and store the watchers if they exist
|
|
||||||
* Then empty every array to remove them
|
|
||||||
* @param elem
|
|
||||||
*/
|
|
||||||
var leaveHimToDie = function (elem) {
|
|
||||||
elem = angular.element(elem);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
function getElemWatchers(element) {
|
|
||||||
wArray[i] = [];
|
|
||||||
wArray[i][0] = getWatchersFromScope(element.data().$isolateScope);
|
|
||||||
wArray[i][1] = getWatchersFromScope(element.data().$scope);
|
|
||||||
|
|
||||||
angular.forEach(element.children(), function (childElement) {
|
|
||||||
i++;
|
|
||||||
getElemWatchers(angular.element(childElement));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWatchersFromScope(scope) {
|
|
||||||
if (scope) {
|
|
||||||
var tmp = scope.$$watchers || [];
|
|
||||||
scope.$$watchers = [];
|
|
||||||
return tmp;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getElemWatchers(elem);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Go through every child elements and restore the watchers from the array we stored with "leaveHimToDie"
|
|
||||||
* @param elem
|
|
||||||
*/
|
|
||||||
var bringHimBack = function (elem) {
|
|
||||||
elem = angular.element(elem);
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
function setElemWatchers(element) {
|
|
||||||
setWatchersFromScope(element.data().$isolateScope, 0);
|
|
||||||
setWatchersFromScope(element.data().$scope, 1);
|
|
||||||
|
|
||||||
angular.forEach(element.children(), function (childElement) {
|
|
||||||
i++;
|
|
||||||
setElemWatchers(angular.element(childElement));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWatchersFromScope(scope, n) {
|
|
||||||
if (scope) {
|
|
||||||
scope.$$watchers = wArray[i][n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the loop
|
|
||||||
setElemWatchers(elem);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var app = angular.module('app', []);
|
||||||
|
|
||||||
|
app.controller('listCtrl', function() {
|
||||||
|
|
||||||
|
var arr = [];
|
||||||
|
|
||||||
|
for (var i=0; i<1000; i++) {
|
||||||
|
arr[i] = {
|
||||||
|
title: 'Element ' + i.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.array = arr;
|
||||||
|
|
||||||
|
this.countWatchers = function() {
|
||||||
|
var root = angular.element(document.getElementsByTagName('body'));
|
||||||
|
|
||||||
|
var watchers = [];
|
||||||
|
|
||||||
|
var f = function (element) {
|
||||||
|
angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) {
|
||||||
|
if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
|
||||||
|
angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
|
||||||
|
watchers.push(watcher);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.forEach(element.children(), function (childElement) {
|
||||||
|
f(angular.element(childElement));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
f(root);
|
||||||
|
|
||||||
|
// Remove duplicate watchers
|
||||||
|
var watchersWithoutDuplicates = [];
|
||||||
|
angular.forEach(watchers, function(item) {
|
||||||
|
if(watchersWithoutDuplicates.indexOf(item) < 0) {
|
||||||
|
watchersWithoutDuplicates.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(watchersWithoutDuplicates.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Disable When Hidden Example</title>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.item {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
background-color: #F2D888;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
position: fixed;
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body ng-app='app'>
|
||||||
|
|
||||||
|
<div ng-controller='listCtrl as list'>
|
||||||
|
|
||||||
|
<button class='button' ng-click='list.countWatchers()'>Console.log watchers</button>
|
||||||
|
|
||||||
|
<div disable-when-hidden>
|
||||||
|
<div ng-repeat="item in list.array" class="item" dwh-element>
|
||||||
|
<p ng-bind='item.title'></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- JS files -->
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
|
||||||
|
<script src="example/main.js"></script>
|
||||||
|
<script src="src/disableWhenHidden.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
||||||
Executable
+148
@@ -0,0 +1,148 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var app = angular.module('app');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First directive, placed on the parent element
|
||||||
|
* Broadcast an event to every relevant child elements every time the user scroll to update the $$watchers situation
|
||||||
|
*/
|
||||||
|
app.directive('disableWhenHidden', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
|
||||||
|
link: function(scope) {
|
||||||
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
var timer = null;
|
||||||
|
return function () {
|
||||||
|
var context = this, args = arguments;
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(context, args);
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var checkElements = debounce(function() {
|
||||||
|
scope.$broadcast('dwhCheckElements');
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
document.addEventListener('scroll', checkElements);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
app.directive('dwhElement', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
|
||||||
|
link: function(scope, element) {
|
||||||
|
|
||||||
|
var m = 1000; // Range of "errors" outside the viewport
|
||||||
|
var wArray = []; // Array to store all the watchers
|
||||||
|
var isHidden = false; // Used to prevent useless computation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store every watchers related to the element
|
||||||
|
* Remove them from the element
|
||||||
|
*/
|
||||||
|
var disableWatchers = function () {
|
||||||
|
wArray.length = 0;
|
||||||
|
leaveHimToDie(element);
|
||||||
|
isHidden = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put back every watchers from the storage array
|
||||||
|
*/
|
||||||
|
var enableWatchers = function () {
|
||||||
|
bringHimBack(element);
|
||||||
|
isHidden = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to know when to enable/disable watchers
|
||||||
|
*/
|
||||||
|
scope.$on('dwhCheckElements', function () {
|
||||||
|
var coordinates = element[0].getBoundingClientRect();
|
||||||
|
if (coordinates.bottom > 0 - m && coordinates.top < window.innerHeight + m) {
|
||||||
|
if (isHidden) {
|
||||||
|
enableWatchers();
|
||||||
|
}
|
||||||
|
} else if (!isHidden) {
|
||||||
|
disableWatchers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*--- Watchers's dance ---*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes through every child elements and store the watchers if they exist
|
||||||
|
* Then empty every array to disable them
|
||||||
|
* @param elem
|
||||||
|
*/
|
||||||
|
var leaveHimToDie = function (elem) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
function getElemWatchers(element) {
|
||||||
|
wArray[i] = [];
|
||||||
|
wArray[i][0] = getWatchersFromScope(element.data().$isolateScope);
|
||||||
|
wArray[i][1] = getWatchersFromScope(element.data().$scope);
|
||||||
|
|
||||||
|
angular.forEach(element.children(), function (childElement) {
|
||||||
|
i++;
|
||||||
|
getElemWatchers(angular.element(childElement));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWatchersFromScope(scope) {
|
||||||
|
if (scope) {
|
||||||
|
var tmp = scope.$$watchers || [];
|
||||||
|
scope.$$watchers = [];
|
||||||
|
return tmp;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getElemWatchers(elem);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go through every child elements and restore the watchers from the array we stored with "leaveHimToDie"
|
||||||
|
* @param elem
|
||||||
|
*/
|
||||||
|
var bringHimBack = function (elem) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
function setElemWatchers(element) {
|
||||||
|
setWatchersFromScope(element.data().$isolateScope, 0);
|
||||||
|
setWatchersFromScope(element.data().$scope, 1);
|
||||||
|
|
||||||
|
angular.forEach(element.children(), function (childElement) {
|
||||||
|
i++;
|
||||||
|
setElemWatchers(angular.element(childElement));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWatchersFromScope(scope, n) {
|
||||||
|
if (scope) {
|
||||||
|
scope.$$watchers = wArray[i][n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the loop
|
||||||
|
setElemWatchers(elem);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user