AngularJS:观察高度变化的更好方法

IT技术 javascript html css angularjs
2021-02-07 07:39:13

我遇到了旧的可变高度导航问题:position: fixes顶部导航和margin-top: $naviHeight下面的内容当数据异步加载时,导航可以改变高度,因此内容的边距必须随之改变。

我希望这是自包含的。所以没有加载数据的代码,而只在涉及的 html-elements/directives 中。

目前,我在 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();
        }
    }

} ] )

这具有使用计时器的明显缺点(我觉得这有点难看)并且在导航调整大小直到内容被移动后会有一定的延迟

有一个更好的方法吗?

6个回答

这是通过注册一个emHeightSource名为 every的观察者来实现的$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();
            } );
        }
    }

} )
调用height()可能会导致reflow性能下降。AngularJS旨在避免DOM在摘要循环期间进行任何访问,而您违反了这一原则。最好有定时器。
2021-03-17 07:39:13
你的意思是在里面emHeightTarget而不是使用事件?这将使代码更简洁,但如果两个指令在不同/隔离的范围内,它不会中断吗?我主要关心的是更多关于计时器的问题。
2021-03-25 07:39:13
谁改变了__height?我认为这行不通,因为一些代码应该更新这个变量。
2021-04-10 07:39:13
emHeightSource 中是否需要 $timeout 依赖注入?似乎您没有引用它。
2021-04-12 07:39:13
@Scheintod 为什么不只检查某些事件的高度变化:当异步数据加载或用户点击某处而不是固定时间间隔时?
2021-04-14 07:39:13

您可以在不使用 Div 的情况下监控元素的高度变化,只需编写一条$watch语句:

// Observe the element's height.
scope.$watch
    (
        function () {
            return linkElement.height();
        },
        function (newValue, oldValue) {
            if (newValue != oldValue) {
                // Do something ...
                console.log(newValue);
            }
        }
    );
这不是从外部传播的。如果我的元素由于某种原因在 angular 之外更改高度,则 $watch 将在下一个摘要循环之前不会触发(例如,当我单击页面时)
2021-03-28 07:39:13
调用height()可能会导致reflow性能下降。AngularJS旨在避免DOM在摘要循环期间进行任何访问,而您违反了这一原则。最好有定时器。
2021-04-14 07:39:13

可能你应该注意$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...

           })
        }
    }    
} ] )
@Scheintod 还有什么可以改变emHeightSource的尺寸?
2021-03-15 07:39:13
不。resize只有当我真正调整浏览器窗口大小时,才会收到事件。但是创意还是可以的。
2021-03-22 07:39:13
就像我写的:异步数据加载。(但还有很多其他的事情。我在一些安静的地方也有这个问题。例如,点击这个把它放在那个里。)
2021-03-25 07:39:13
为了使这种情况具体化:这是一个面包屑栏,其路径可能会变得很长,以至于换行。页面内容及其面包屑路径是通过 json 调用获取的。
2021-03-29 07:39:13
@Scheintod 我明白了,所以最好的解决方案是 CodeHater 建议的:在异步事件和用户交互之后检查高度。问题是如何在指令中做到这一点
2021-04-01 07:39:13

我使用了 $watch 和 resize 事件的组合。我发现没有 scope.$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();
        });
    };

这种方法避免(可能)触发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
)