【前端框架】Element UI Dialog 组件中执行 DOM 操作异常问题的分析与处理
文章目录
- 1 问题描述
- 2 问题分析
- 3 解决方案
- 3.1 将元素放在 footer slot 中
- 3.2 在 open 事件中延迟执行具体的代码
- 3.3 在 update 回调函数执行相关的逻辑
- 3.4 主动更改 rendered 的值为 true
1 问题描述
使用 Element UI dialog 组件,在 dialog 对话框中渲染图表,需要获取图表挂载点的 dom 元素。由于 Element UI 的 dialog_body 是以 lazy 模式进行渲染,导致 dialog 打开时,图表加载失败!
示例页面
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><!-- import CSS --><link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body><div id="app"><el-button @click="visible = true">Button</el-button><el-dialog :visible.sync="visible" @open="handleOpen" title="Hello world"><div id="hello"><p>Try Element</p></div></el-dialog></div>
</body><!-- import Vue before Element --><script src="https://unpkg.com/vue/dist/vue.js"></script><!-- import JavaScript --><script src="https://unpkg.com/element-ui/lib/index.js"></script><script>var vm = new Vue({el: '#app',data: function() {return { visible: false }},methods: {handleOpen(){console.log("open: "+$("#hello p").html()); // TAG 1}}})</script>
</html>
element ui dialog 组件提供了 open 事件,在 Dialog 打开时执行回调。
官网对 open 的解释如下
Dialog 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上。因此,如果需要执行 DOM 操作,或通过 ref 获取相应组件,请在 open 事件回调中进行。
可是实际使用时,发现并非如此。
在上例 TAG 1 所处的时间点,实际页面如下:
<div id="app"><button type="button" class="el-button l-button--default"><span>Button</span></button> <div class="el-dialog__wrapper" style="display: none;"><div class="el-dialog" style="margin-top: 15vh;"><div class="el-dialog__header"><span class="el-dialog__title">Hello world</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><div class="el-dialog__footer"><div>Hello Hello</div></div></div></div>
</div>
可见,这个时候的页面并不完整,是无法通过选择器获取 dom 元素的,那么对 “#hello” dom 元素的所有操作都是无效的。
此时检测到 dialog 组件的状态如下:
vm.visible // true
vm.$children[1].visible // true
vm.$children[1].rendered // true
vm.$children[1].opened // false
2 问题分析
el-dialog 组件会监听 visible 的状态,当状态为 true 时,立刻触发 open 事件,但这个时候 el-dialog__body 的内容还没有渲染。因为 Vue 组件通过 $emit 触发的事件并不是异步执行的,而是同步执行。
代码如下:
watch: {visible: function(e) {var t = this;e ? (this.closed = !1,this.$emit("open"),this.$el.addEventListener("scroll", this.updatePopper),this.$nextTick(function() {t.$refs.dialog.scrollTop = 0}),this.appendToBody && document.body.appendChild(this.$el)) : (this.$el.removeEventListener("scroll", this.updatePopper),this.closed || this.$emit("close"))}}
正如官方文档所说,Dialog 的内容,也就是 el-dialog__body 中的内容是懒加载的!
那么它懒加载的机制是什么呢?
查看 element dialog 组件的代码,会发现:
<div class="el-dialog__body" v-if="rendered"><slot></slot></div>
Vue 中 v-if 指令的特征是,根据表达式的值在 DOM 中生成或移除一个元素,如果 rendered 为 false,那么该 dom 元素会被移除,当 rendered 为 true 时,对应元素的克隆会被重新插入到 DOM 中。 而 v-show 指令则是根据表达式的值来显示或隐藏 HTML 元素,并不会移除 DOM 元素。
v-if 是惰性的,如果初始渲染时条件为假,则什么也不做,在条件第一次变为真时才开始局部编译(编译结果会缓存起来)。
这就是 element dialog-body 懒加载的原理。
3 解决方案
3.1 将元素放在 footer slot 中
dialog 组件中的 footer slot 是实时渲染的,放在其中的 dom 元素可以直接获取:
<div id="app"><el-button @click="visible = true">Button</el-button><el-dialog :visible.sync="visible" @open="handleOpen" title="Hello world"><div slot="footer"><div id="hello"><p>Try Element</p></div></div></el-dialog></div>
3.2 在 open 事件中延迟执行具体的代码
由于浏览器是单线程执行前端代码,因此可以短暂的交出控制权,让其进行渲染页面,然后再执行具体的任务。
var vm = new Vue({el: '#app',data: function() {return { visible: false }},methods: {handleOpen(){setTimeout(console.log, 100, "open: "+$("#hello p").html()) // TAG 1}}})
3.3 在 update 回调函数执行相关的逻辑
当 v-if 为 true 时,会重新渲染页面,渲染结束后会触发 update 回调函数,这个时候可以用来执行一些代码。
var vm = new Vue({el: '#app',data: function() {return { visible: false,see: true,nosee: false}},updated: function(){var dialog = this.$children[1]if(dialog.rendered && this.visible) {console.log("update: " + $("#hello p").html())}}})
需要注意的时,要对 dialog 的状态进行验证,确保代码的执行时机准确无误。
3.4 主动更改 rendered 的值为 true
在 open dialog 之前,先将 rendered 的值改为 true
var vm = new Vue({el: '#app',data: function() {return { visible: false }},methods: {handleOpen(){console.log("open: "+$("#hello p").html()); // TAG 1}}})vm.$children[1].rendered = true
可能还有更简洁、高效的方式,知道的朋友可以告知一下,相互学习。
【前端框架】Element UI Dialog 组件中执行 DOM 操作异常问题的分析与处理相关推荐
- 前端笔记—第15篇js中的DOM操作
获取事件源 document.getElementById() document.getElementsByClass() document.getElementsByTagName() docume ...
- 前端漫漫-Element UI报错:Unknown custom element: <el-menu>
前端漫漫-Element UI报错:Unknown custom element: 问题描述:编写VUE文件,从Element UI官网复制组件信息的时候,引用了Element UI的html标签,但 ...
- 开源的HTML5前端框架Amaze UI发布,推进mobile first前端Web方案
2014年6月,云适配发布了开源的HTML5前端框架Amaze UI,目前处于内测期.根据Amaze UI的官网介绍,该框架的特点是mobile first,解决Web应用从PC向多屏适配的问题,兼容 ...
- element ui table组件扩展关于列表编辑按钮的位置放置
最近在用vue做项目,主要是用的element ui的组件,在用的过程中发现有部分组件需要扩展,改源码太折腾,成本高,就想着如何节省成本来实现这些需求,由于项目时间紧张,有些实现来也没来得及记录一下, ...
- 实战 | Element UI 父子组件传值与事件绑定(正向)
这是小小的本周的第三篇,本篇将会讲解关于Element UI 父子组件传值与事件绑定. 父子组件传值 新建父组件和子组件 新建父组件 代码如下 <template><div id=& ...
- element ui upload组件文件上传一次 后,不论是上传成功之后修改文件再上传还是上传失败重新上传,再次点击上传均无反应。
问题: Element UI Upload 组件文件上传一次 后,不论是上传成功之后修改文件再上传还是上传失败重新上传,再次点击上传均无反应. 原因: 第一次上传文件后,浏览器还保存着我们已经上传的文 ...
- Vue Element UI 表格组件 利用插槽实现按下按钮后获得本行数据(内容)
Vue Element UI 表格组件 利用插槽实现按下按钮后获得本行数据(内容) 能够解决的问题 需要在表格中添加一个类似修改或编辑的按钮,按下按钮,弹出的窗口需要本行的渲染数据 需要向服务端提交一 ...
- Element UI——滚动条组件(ElScrollBar)修改.el-scrollbar__wrap和el-scrollbar__view的CSS属性
基本概念 el-scrollbar:Element UI隐藏组件. 注意事项: 1.el-scrollbar的父层要有固定高度 2.el-scrollbar的高度要设成100% 3.如果出现横滚动条, ...
- 实战 | Element UI 父子组件传值与事件绑定(逆向)
这是小小本周的第四篇,本篇将会倒过来讲解Element UI 父子组件传值与事件绑定. 父子组件传值 新建父组件和子组件 新建父组件 代码如下 <template><div id=& ...
- 响应式滚动图懒加载 element ui el-carousel 组件优化代码
响应式滚动图懒加载 element ui el-carousel 组件优化代码 懒加载插件vue-lazyload //main.js import VueLazyload from 'vue-la ...
最新文章
- 为什么分布式一定要有一致性方案?
- 大数据系统软件国家工程实验室给大家拜年啦!
- dataTable的用法
- vue 代理设置 访问图片_详解Vue源码之数据的代理访问
- C#2.0泛型中的变化: default 关键字
- 【解决方案】jquery live的change事件在IE下失效
- Linux内核学习笔记(2)-- 父进程和子进程及它们的访问方法
- extjs初学者教程
- 最新苹果开发者账号添加设备UDID
- 农业统计分析系列2-试验设计
- 程序员们的那些神表情,有木有击中你^_^
- R语言画个中国地图使用shp文件
- CSGO手套武器箱直接卖还是开了再卖?
- IntelliJ IDEA注释字体形式修改
- 免费UI色彩搭配素材资源|色卡帮你找准搭配技巧
- Rman配置DataGuard using Backup-based duplication with a target connection with filesystem
- 能帮你找到网页设计灵感的16个网站-沪江UI团队资料翻译组
- element组件---Form
- 医疗器械系统软件转产方案
- 史上最全的Android面试题集锦