【问题标题】:In Angular JS, is there a way to watch for changes to the DOM without using scope.watch?在 Angular JS 中,有没有办法在不使用 scope.watch 的情况下监视 DOM 的变化?
【发布时间】:2026-01-16 15:15:01
【问题描述】:

我正在尝试创建一个 angularjs 引导手风琴,它在打开时将手风琴滚动到顶部。

这些解决方案与我想做的很接近:

但是,它们使用超时或范围监视。除非绝对必要,否则我想避免使用这些。

有没有办法在不使用$watchsetTimeout 的情况下做到这一点?

这是我正在尝试做的事情,这是使用 $watch:https://plnkr.co/edit/XQpUdrdjqaCGom4L9yIJ

app.directive( 'scrollTop', scrollTop );

function scrollTop() {
    return {
        restrict: 'A',
        link: link
    };
}

function link( scope, element ) {
    scope.collapsing = false;
    var jqElement = $( element) ;
    scope.$watch( function() {
        return jqElement.find( '.panel-collapse' ).hasClass( 'collapsing' );
    }, function( status ) {
        if ( scope.collapsing && !status ) {
            if ( jqElement.hasClass( 'panel-open' ) ) {
                $( 'html,body' ).animate({
                    scrollTop: jqElement.offset().top - 30
                }, 500 );
            }
        }
        scope.collapsing = status;
    } );
}

【问题讨论】:

  • 可能是MutationObserver
  • 你能告诉我们你的代码和手表吗?
  • @Dalorzo 我已经用 plunkr 代码更新了我的问题

标签: angularjs angularjs-directive angularjs-scope angular-ui-bootstrap


【解决方案1】:

指令可以简化为:1

app.directive( 'scrollTop', function scrollTop($timeout) {
    return {
        restrict: 'A',
        link: postLink
    };
    function postLink(scope, elem, attrs) {
        elem.on("click", function(e) {
          if (scope.status.isOpen) {
            $timeout(function() {
              $( 'html,body' ).animate({
                scrollTop: elem.offset().top - 30
              }, 500 );
            });
          }
        });
    }
})
<uib-accordion>
  <div heading="Section Title" is-open="status.isOpen"
       ng-repeat="section in vm.sections"
       scroll-top
       uib-accordion-group>
    <uib-accordion-heading>
      <div ng-class="{isOpen: vm.isOpen}">
        <h3>{{section.sectionTitle}}</h3>
        <p>{{section.sectionSubHeader}}</p>
      </div>
    </uib-accordion-heading>
    <div class="clearfix">
      <b>Index+1={{$index+1}}</b> 
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
      in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </div>
  </div>
</uib-accordion>

$timeout 是必要的,因为浏览器需要使用新打开和关闭的元素渲染 DOM,然后才能计算正确的滚动偏移量。

DEMO on PLNKR

【讨论】:

  • 是否可以通过使用组件生命周期的$doCheck来避免使用$timeout?我在其他控制器中使用 $doCheck 来使用异步内容更新 DOM。我还想避免使用$scope 变量,并尽可能使用控制器范围变量。
【解决方案2】:

我已经找到了一种从控制器中执行此操作的方法。

我添加了一个在ng-click 上触发的函数来报告手风琴的is-open 状态。

使用组件生命周期钩子$doCheck 我能够观察到vm.isOpen 状态的变化。 $doCheck 在每个摘要周期结束时运行,所以我不需要设置 $scope.watch$timeOut

$doCheck 运行的代码与问题中的指令基本相同

app.controller('homeController', function($state, $element, sections, $transitions) {
  var vm = this;

  vm.$onInit = function() {
    vm.sections = sections.getSections();
  };

  function updateOpenStatus() {
    vm.collapsing = false;
    vm.isOpen = vm.sections.some(function(item) {
      return item.isOpen;
    });
  }

  vm.$doCheck = function() {
    if (vm.isOpen) {
      var elem = $element.find('.panel-collapse');
      var status = elem.hasClass('collapsing');
      if (vm.collapsing && !status) {
        var parentElem = elem.closest('.panel-open');
        if (elem.parent().hasClass('panel-open')) {
          $('html,body')
            .stop()
            .animate({
              scrollTop: parentElem.offset().top - 52
            }, 'fast');
        }
      }
      vm.collapsing = status;
    }
  };
});

我更新了uib-accordion 以调用控制器中的函数

<uib-accordion>
  <div heading="Section Title" is-open="section.isOpen" ng-repeat="section in vm.sections" scroll-top uib-accordion-group>
    <uib-accordion-heading>
      <div ng-class="{isOpen: section.isOpen}" ng-click="vm.toggleOpen()">
        <h3>{{section.sectionTitle}}</h3>
      </div>
    </uib-accordion-heading>
    <div class="clearfix">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
      in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </div>
  </div>
</uib-accordion>

更新的 Plnkr:https://plnkr.co/edit/5EqDfmVOa0hzFfaQqdI0?p=preview

【讨论】:

    最近更新 更多