当前位置: 首页 > article >正文

WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响的问题之Shadow DOM

前情提要

前面我写过一篇文章讲这个事情。用的是 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 模板。这个模板的内容加载到浏览器里面后,浏览器是不会显示的。模板的代码如下:

<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 的网页元素包起来,就是一个模板了。

使用模板在页面里真正创建和显示这个元素

            <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 获得用户输入的数据,然后给外面的表单提交。代码如下:

       <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 的模板文件里面声明和创建实例的。这里仅仅是调用它。

煞科

到这里,整个页面就做好了。实现了:

  1. 代码封装;把 Editor 单独放到一个文件里面,加上 Template 套起来。
  2. CSS 隔离:把 Editor 放到 Shadow DOM 里面,防止被页面的 CSS 污染。
  3. 提交:用 JavaScript 把用户在 Editor 里面输入的内容提取出来,放到一个表单内部的隐藏字段里面,就可以正确提交了。一个在 Shadow DOM 里面创建的 Editor 对象,只要创建时对象是一个 JavaScript 的全局变量,在其它地方也是可以调用到的。

吐个槽

前端代码的语法真是狗屎。像 CSS 这种完全没有作用域和封装的概念,需要用 Shadow DOM 或者 iframe 之类的技巧来避开语法的坑,完全属于奇巧淫技的做法,属于野路子,非正常写代码的路子。真是可怜成天专门写前端代码的童子。

各位,看完本文如果觉得有点干货内容,点个赞吧。


http://www.kler.cn/news/321231.html

相关文章:

  • PostgreSQL 向量数据存储指南
  • 即梦PixelDance:从追赶到领跑,一跃成为全球AI竞赛的领航者!
  • 付费计量系统的标准化框架(下)
  • PCIe扫盲(14)
  • 树莓派基础命令
  • Keil5安装arm和C51共存环境
  • SSC338D/SSC338Q CA7*2+IPU5M/Multi-sensorISP: HDR/3DNR
  • 一键转换:Python如何轻松将PPT转换为PDF
  • Spring(三)Spring事件+计划定时任务+条件注解+SpringAware
  • 详细解释在Android开发中如何实现自定义View
  • Vue.js入门
  • “AI+Security”系列第3期(二):AI赋能自动化渗透测试
  • GPT实现联网,NextChat插件的配置说明
  • MySQL高阶1853-转换日期格式
  • 手机也可以更换任意IP地址吗?
  • 【算法】分治:归并之 912.排序数组(medium)
  • LLM - 使用 XTuner 指令微调 多模态大语言模型(InternVL2) 教程
  • 【devops】devops-ansible之介绍和基础使用
  • Safari-常用快捷键(IPadOS版本)
  • 一体化平台数据中心安全建设方案(Word完整原件)
  • Fabric V2.5 通用溯源系统——使用Hyperledger Caliper压力测试
  • 体验鸿蒙开发第一课
  • Linux标准IO(二)-打开、读写、定位文件
  • 【RabbitMQ】消息堆积、推拉模式
  • java调用opencv部署到centos7
  • 个人行政复议在线预约系统开发+ssm论文源码调试讲解
  • TypeScript 中的接口、泛型与自定义类型
  • 基于Es和智普AI实现的语义检索
  • <script>中的为什么需要转义?
  • 【python qdrant 向量数据库 完整示例代码】