AngularJS: лучший способ наблюдать за изменением высоты
У меня старая проблема с навигацией по высоте: A position: fixes
навигация сверху и содержимое с margin-top: $naviHeight
ниже.
Навигационная система может изменять высоту, когда данные загружаются асинхронно, и поэтому поле содержимого должно меняться вместе с ней.
Я хочу, чтобы это было самодостаточным. Таким образом, нет кода, в котором данные загружаются, но только в задействованных html-элементах/директивах.
В настоящее время я делаю это в AngularJS 1.2.0 с таким таймером:
/*
* Get notified when height changes and change margin-top
*/
.directive( 'emHeightTarget', function(){
return {
link: function( scope, elem, attrs ){
scope.$on( 'heightchange', function( ev, newHeight ){
elem.attr( 'style', 'margin-top: ' + (58+newHeight) + 'px' );
} );
}
}
})
/*
* Checks this element periodically for height changes
*/
.directive( 'emHeightSource', ['$timeout', function( $timeout ) {
return {
link: function( scope, elem, attrs ){
function __check(){
var h = elem.height();
if( h != scope.__height ){
scope.__height = h;
scope.$emit( 'heightchange', h );
}
$timeout( __check, 1000 );
}
__check();
}
}
} ] )
У этого есть очевидный недостаток использования таймера (который я чувствую как-то некрасиво) и определенную задержку после изменения размера навигации, пока содержимое не будет перемещено. p >
Есть ли лучший способ сделать это?
Ответы
Ответ 1
Это работает, регистрируя наблюдателя в emHeightSource
, который вызывается каждый $digest
. Он обновляет свойство __height
, которое в свою очередь просматривается в emHeightTarget
:
/*
* Get notified when height changes and change margin-top
*/
.directive( 'emHeightTarget', function() {
return {
link: function( scope, elem, attrs ) {
scope.$watch( '__height', function( newHeight, oldHeight ) {
elem.attr( 'style', 'margin-top: ' + (58 + newHeight) + 'px' );
} );
}
}
} )
/*
* Checks every $digest for height changes
*/
.directive( 'emHeightSource', function() {
return {
link: function( scope, elem, attrs ) {
scope.$watch( function() {
scope.__height = elem.height();
} );
}
}
} )
Ответ 2
Вы можете контролировать изменение высоты элемента без использования Div, просто пишите инструкцию $watch
:
// Observe the element height.
scope.$watch
(
function () {
return linkElement.height();
},
function (newValue, oldValue) {
if (newValue != oldValue) {
// Do something ...
console.log(newValue);
}
}
);
Ответ 3
Возможно, вам стоит посмотреть изменения размеров $window
', например:
.directive( 'emHeightSource', [ '$window', function( $window ) {
return {
link: function( scope, elem, attrs ){
var win = angular.element($window);
win.bind("resize",function(e){
console.log(" Window resized! ");
// Your relevant code here...
})
}
}
} ] )
Ответ 4
Я использовал комбинацию $watch и resize event. Я нашел без ограничений. $Apply(); в событии изменения размера изменения высоты элемента не всегда подхватываются $watch.
link:function (scope, elem) {
var win = angular.element($window);
scope.$watch(function () {
return elem[0].offsetHeight;
},
function (newValue, oldValue) {
if (newValue !== oldValue)
{
// do some thing
}
});
win.bind('resize', function () {
scope.$apply();
});
};
Ответ 5
Этот подход позволяет избежать (потенциально) запуска reflow
каждого цикла дайджеста. Он проверяет только elem.height()
/after/цикл дайджеста, и только вызывает новый дайджест, если высота изменилась.
var DEBOUNCE_INTERVAL = 50; //play with this to get a balance of performance/responsiveness
var timer
scope.$watch(function() { timer = timer || $timeout(
function() {
timer = null;
var h = elem.height();
if (scope.height !== h) {
scope.$apply(function() { scope.height = h })
}
},
DEBOUNCE_INTERVAL,
false
)
Ответ 6
Я написал еще один вариант, основанный на таймере, который может быть привязан к области видимости, потому что, как правильно заявил Джейми Паэт, прямой доступ DOM внутри цикла дайджеста - не очень хорошая идея.
.directive("sizeWatcher", ['$timeout', function ($timeout) {
return {
scope: {
sizeWatcherHeight: '=',
sizeWatcherWidth: '=',
},
link: function( scope, elem, attrs ){
function checkSize(){
scope.sizeWatcherHeight = elem.prop('offsetHeight');
scope.sizeWatcherWidth = elem.prop('clientWidth');
$timeout( checkSize, 1000 );
}
checkSize();
}
};
}
Теперь вы можете привязать его к любому элементу:
<img size-watcher size-watcher-height="myheight">
<div style="height: {{ myheight }}px">
Таким образом, div всегда сохраняет (с латентностью секунды) ту же высоту, что и изображение.
Ответ 7
Этот подход отслеживает высоту и ширину элементов и присваивает их переменной в области, указанной в атрибуте ваших элементов
<div el-size="size"></div>
.directive('elSize', ['$parse', function($parse) {
return function(scope, elem, attrs) {
var fn = $parse(attrs.elSize);
scope.$watch(function() {
return { width: elem.width(), height: elem.height() };
}, function(size) {
fn.assign(scope, size);
}, true);
}
}])