前情提要
前面我写过一篇文章讲这个事情。用的是 iframe 的方法。文章链接:
WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响的问题-CSDN博客
问题来了
使用 iframe 确实可以在框架页面有 Pico.css 的情况下,在 iframe 里面的 Quill Editor 不受影响。但是,编辑器里面用户输入的内容,是需要提交到服务器端的。因此,编辑器是嵌套在一个 <form> <div id="MyEditor"></form> 里面的。同时这个 Form 里面还要有一个提交按钮。
如果仅仅是提交编辑器里面的内容,那这样做就没有问题。但是,可能还有单独的文章表,等等其它需要用户输入的东西,要一起提交。比如文章标题:<input type="text" id="MySubject">, 而这个输入框是需要被 Pico.css 给予样式的。如果把这些输入框和 Editor 一起放进 iframe 里面,则无法接受 Pico.css 的样式!
采用 Shadow DOM 来解决这个问题
Shoadow DOM 的基本概念
页面上很多元素,输入框,按钮,等等,构成页面的 DOM。
页面上还可以有 Shadow DOM,挂载在 DOM 的某个节点底下。在 Shadow DOM 里面的页面元素比如一个<input type="text"> 或者一个按钮比如 <button> 也是正常显示在页面上的,但是,这些元素不受页面上的 CSS 的影响。相当于屏蔽掉了页面上的 CSS;
具体操作
思路
我们是把 Editor 放到 <div id="MyEditor"></div> 里面的。我们把这个部分,放进 Shadow DOM;
然后在页面,也就是 Shadow DOM 外面,有一个表单,表单内部有提交按钮,以及文章标题输入框。那么,这些,当提交按钮被点击,就可以提交表单内容。
问题
如果把表单(<form>)放到 Shadow DOM 外面,那么,提交按钮如何获取到在 Shadow DOM 里面的 MyEditor 的用户输入的内容?
如果把一个页面元素比如 quill Editor 放进 Shadow DOM
首先,把 quill Editor 相关的 HTML 代码,放进 HTML 模板。这个模板的内容加载到浏览器里面后,浏览器是不会显示的。模板的代码如下:
html
<template id="my-element">
<!-- 用 template 来封装这个编辑器,避免它被页面上的其它 CSS 污染 -->
<!-- quill 编辑器的封装 https://quilljs.com/ -->
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js">
</script>
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css"
rel="stylesheet">
<style>
.edit_container { font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
text-align: center; color: #2c3e50; margin-top: 60px; } .ql-editor{ height:400px;
}
</style>
<div id="editor1">
<h2>
这是编辑框里面的内容,服务器端加载内容可以直接填到这里。
<#AContent>
这里替换
</h2>
</div>
<script>
const MyContainer = host.shadowRoot.querySelector("#editor1");
const quill = new Quill(MyContainer, {
modules: {
toolbar: [[{
header: [1, 2, false]
}], ['bold', 'italic', 'underline'], ['image', 'code-block'], ],
},
theme: 'snow'
});
</script>
</template>
其实就是用 <template > 这个标记来把一个 HTML 的网页元素包起来,就是一个模板了。
使用模板在页面里真正创建和显示这个元素
html
<div id="MyHost">
</div>
<script>
const host = document.querySelector("#MyHost");
const shadow = host.attachShadow({
mode: "open"
});
const template = document.getElementById("my-element");
shadow.appendChild(template.content);
</script>
首先定义一个容器,MyHost 然后用代码,把模板里面的 HTML 代码描述的 HTML 元件挂在这个 MyHost 的底下,作为它的 Shadow DOM。如果页面显示正常,打开浏览器的开发者工具,查看页面元素,会看到开发者工具里面,在 MyHost 那个 DIV 底下,会显示【shadow-root】的字样,再底下才是这个 Editor 的代码。
解决提交的问题
表单(<form>)和提交按钮,放在页面里面,而不是 Shadow DOM 里面。这样按钮才能获得 PICO.CSS 提供的样式。同样需要提交的文章的标题栏,也在这个表单里面,也能够获得样式。
但是,Editor 不在这个表单里面。如果把表单和 Editor 放在一起,那就必须把标题输入框和按钮也和 Editor 放一起,那这两个玩意就无法获得页面上的 PICO.CSS 提供的样式了。
因此,我要用 JavaScript 代码,从 Shadow DOM 里面的 Editor 获得用户输入的数据,然后给外面的表单提交。代码如下:
html
<form id="myForm" action="ContentUpdate" method="post">
<input type="hidden" name="delta" id="deltaInput">
<input type="hidden" name="html" id="htmlInput">
<input type="text" name="MySubject1" id="MySubject1" value="我的标题">
<input type="submit" value="提交">
</form>
<script>
const host = document.querySelector("#MyHost");
const shadow = host.attachShadow({
mode: "open"
});
const template = document.getElementById("my-element");
//把页面模板放到容器的 Shadow DOM 里面去。
shadow.appendChild(template.content);
//监听提交按钮,把提交内容复制到隐藏字段里面再提交,服务器读隐藏字段值
const MyEditor = host.shadowRoot.querySelector('#editor1');
//if not MyEditor alert("没找到编辑");
//const quill = host.quillInstance;
document.getElementById('myForm').addEventListener('submit',
function() {
// 获取 Quill 编辑器的内容
const delta = JSON.stringify(quill.getContents());
const html = quill.root.innerHTML;
//delta = '';
//html = MyEditor.outerHTML;
// 将内容放入隐藏输入框中
document.getElementById('deltaInput').value = delta;
document.getElementById('htmlInput').value = html;
});
</script>
注意,上述代码里面的 quill 是在 Editor 的模板文件里面声明和创建实例的。这里仅仅是调用它。
煞科
到这里,整个页面就做好了。实现了:
- 代码封装;把 Editor 单独放到一个文件里面,加上 Template 套起来。
- CSS 隔离:把 Editor 放到 Shadow DOM 里面,防止被页面的 CSS 污染。
- 提交:用 JavaScript 把用户在 Editor 里面输入的内容提取出来,放到一个表单内部的隐藏字段里面,就可以正确提交了。一个在 Shadow DOM 里面创建的 Editor 对象,只要创建时对象是一个 JavaScript 的全局变量,在其它地方也是可以调用到的。
吐个槽
前端代码的语法真是狗屎。像 CSS 这种完全没有作用域和封装的概念,需要用 Shadow DOM 或者 iframe 之类的技巧来避开语法的坑,完全属于奇巧淫技的做法,属于野路子,非正常写代码的路子。真是可怜成天专门写前端代码的童子。
各位,看完本文如果觉得有点干货内容,点个赞吧。