使用 MutationObserver 检测滚动高度变化?

Posted

技术标签:

【中文标题】使用 MutationObserver 检测滚动高度变化?【英文标题】:Detect scrollHeight change with MutationObserver? 【发布时间】:2017-11-09 17:38:15 【问题描述】:

如何使用MutationObserver 检测 DOM 元素上的 scrollHeight 何时更改?它不是属性,也不是数据。

背景:我需要检测滚动条何时出现在我的内容元素上,其中overflow-y 设置为自动。我想在滚动条出现的那一刻,scrollHeight 的值会从 0 跳到比如说 500,所以我的想法是设置一个MutationObserver 来检测这个属性的变化。

到目前为止我得到了什么:

HTML

<div class="body" #body>

CSS

.body 
    overflow-y: auto;

TypeScript

export class MyWatchedContent implements AfterViewInit, OnDestroy 
   @ViewChild('body',  read: ElementRef )
   private body: ElementRef;

   private observer: MutationObserver;

   public ngAfterViewInit() 
       this.observer = new MutationObserver(this.observerChanges);
       this.observer.observe(this.body.nativeElement, 
           attributes: true,
       );
   

   public ngOnDestroy() 
       this.observer.disconnect();
   

   private observerChanges(records: MutationRecord[], observer: MutationObserver) 
       console.log('##### MUTATION');
       records.forEach((_record) => 
           console.log(_record);
       );
   

例如,如果我更改开发者窗口中的背景颜色,我可以看到观察者正在触发

突变

my-content-watcher.component.ts?d0f4:233 MutationRecord type: "attributes", target: div.body, addedNodes: NodeList(0), removedNodes: NodeList(0), previousSibling: null…

但是,如果我更改窗口大小以使滚动条出现,则没有检测到突变。这对MutationObserver 是否可行?如果可以,如何?

【问题讨论】:

MutationObserver 不会检测到它。尝试 ResizeObserver 或只是定期检查。 @wOxxOm 甚至 ResizeObserver 都没有检测到 scrollHeight 的变化。 嗯,IntersectionObserver 可能会有所帮助。 ResizeObserver 可能无法检测 scrollHeight,但它会检测内容高度。如果您正在观看会增长的内容本身,应该可以使用它。 【参考方案1】:

以下是答案,适合仍在寻找解决方案的任何人:

截至今天,无法直接监控元素的 scrollHeight 变化

MutationObserver 检测 DOM 树中的变化,这可能表明 scrollHeight 发生了变化,但这是一个疯狂的猜测。 ResizeObserver 检测元素外部高度的变化,但不检测滚动高度(即“内部”高度)。 目前还没有 ScrollHeight-Observer

但解决方案非常接近:

解决方案

ResizeObserver 检测元素外部高度的变化...

观察滚动容器没有意义,因为它的外部高度没有改变。改变其出高度的元素是容器的任何 CHILD 节点!

一旦子节点的高度发生变化,就意味着父容器的scrollHeight发生了变化。

原生 JS 版本

const container = document.querySelector('.scrollable-container');

const observer = new ResizeObserver(function() 
    console.log('New scrollHeight', container.scrollHeight);
);

// This is the critical part: We observe the size of all children!
for (var i = 0; i < container.children.length; i++) 
    observer.observe(container.children[i]);
)

jQuery 版本

const container = $('.scrollable-container');

const observer = new ResizeObserver(function() 
    console.log('New scrollHeight', container[0].scrollHeight);
);

container.children().each(function(index, child) 
    observer.observe(child);
);

进一步的步骤

当子节点被动态添加时,您可以添加一个 MutationObserver 以便在添加新子节点后将其添加到 ResizeObserver。

【讨论】:

元素可能具有动态内容,这意味着动态添加/删除子项。最好将ResizeObserverMutationObserver 结合使用,以涵盖内部内容更改和容器实际大小调整的所有情况【参考方案2】:

您可以通过在contenteditable 元素上添加一个内部包装元素(例如span)来模拟此行为,然后在内部元素上添加ResizeObserver 侦听器。内部span 必须是display:block 否则不会触发ResizeObserver

html

<div contenteditable id="input"><span id="content">Some content</span></div>

CSS

#input 
  max-height: 100px;
  overflow: scroll;
  white-space: pre-wrap;


#content 
  display: block;
  white-space: pre-wrap;

JS

const content = document.getElementById("content");
const observer = new ResizeObserver((entries) => 
  for (const entry of entries) 
    console.warn(entry);
  
);

observer.observe(content);

【讨论】:

以上是关于使用 MutationObserver 检测滚动高度变化?的主要内容,如果未能解决你的问题,请参考以下文章

MutationObserver 在整个 DOM 中检测节点的性能

javascript 检测dom变化#MutationObserver #js

javascript 检测dom变化#MutationObserver #js

MutationObserver 类更改

Mutation Observer 无法检测到元素的 dom 移除

UICollectionView:如何检测滚动何时停止