Angular v10中如何实现DOM子元素跨容器移动
问题场景
- 基于Angular v10开发应用,现有一个公共共享组件通过
ng-content接收外部传入的内容,传入内容通常为div、按钮等元素组成的列表。 - 需要新增宽度适配逻辑:当原容器宽度不足时,将原容器的最后一个子元素移动到指定的其他容器中。
- 当前进度:已经实现待移动最后一个元素的数组存储、从原容器移除的逻辑,但无法将存储的元素成功添加到目标容器。
- 已尝试的无效方案:
- 尝试通过
innerHTML属性实现元素插入,页面仅显示对象文本,没有渲染为预期的HTML元素 - 尝试通过
push或重新赋值对象的方式,将存储移动元素的movedItems数组设置为新容器的子元素,逻辑未生效
- 尝试通过
失效原因说明
innerHTML方案失效:innerHTML属性仅接收HTML格式的字符串入参,传入DOM节点对象时,JS会自动将对象转为字符串,最终展示为[object HTMLElement]这类对象文本,自然无法渲染为正常元素。- 数组赋值配合
*ngFor方案失效:*ngFor是基于结构化数据渲染视图的指令,本身无法识别存入数组的DOM节点引用,没法把DOM节点直接当作模板内容渲染。
可行实现方案
对于ng-content传入的原生内容,直接操作原生DOM节点移动是成本最低、不会丢失原有事件绑定、指令逻辑的方案,不需要额外存数组做中转:
- 首先在组件类中通过
ViewChild获取原容器和目标容器的DOM引用 - 触发宽度适配逻辑时,直接取原容器的最后一个子节点,通过原生
appendChild方法插入目标容器即可(appendChild方法会自动将节点从原DOM位置移动到新位置,不需要手动执行移除操作)
参考代码如下:
组件类代码
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; @Component({ selector: 'app-share-component', templateUrl: './share-component.html' }) export class ShareComponent implements AfterViewInit { // 获取原容器(放ng-content的容器)的DOM引用 @ViewChild('originalContentBox', { static: true, read: ElementRef }) originalBox: ElementRef<HTMLElement>; // 获取溢出元素存放的目标容器引用 @ViewChild('overflowBox', { static: true, read: ElementRef }) overflowBox: ElementRef<HTMLElement>; ngAfterViewInit() { // 可绑定ResizeObserver监听容器宽度变化,自动触发移动逻辑 const resizeObserver = new ResizeObserver(entries => { for (const entry of entries) { if (entry.contentRect.width < 500) { this.moveLastItemToOverflow(); } else { this.restoreItemFromOverflow(); } } }); resizeObserver.observe(this.originalBox.nativeElement); } // 移动最后一个元素到溢出容器 moveLastItemToOverflow() { const originalEl = this.originalBox.nativeElement; const overflowEl = this.overflowBox.nativeElement; const lastChild = originalEl.lastElementChild; if (lastChild) { // appendChild会自动将节点从原有位置移动到新位置 overflowEl.appendChild(lastChild); } } // 将溢出容器的元素移回原容器(适配宽度恢复的场景) restoreItemFromOverflow() { const originalEl = this.originalBox.nativeElement; const overflowEl = this.overflowBox.nativeElement; const firstMovedChild = overflowEl.firstElementChild; if (firstMovedChild) { originalEl.appendChild(firstMovedChild); } } }
组件模板代码
<div class="content-wrapper"> <!-- 原内容容器,ng-content放在这里 --> <div #originalContentBox class="original-box"> <ng-content></ng-content> </div> <!-- 溢出元素存放的目标容器 --> <div #overflowBox class="overflow-box"></div> </div>
注意:如果需要实现更精准的溢出判断(比如判断子元素是否实际超出容器边界而非单纯判断容器宽度),可以通过计算每个子元素的offsetLeft + offsetWidth和容器宽度对比,确定需要移动的元素范围。
内容的提问来源于stack exchange,提问作者ineedtoknow
相关产品推荐
相关产品推荐

