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

时间:2024-10-02 16:10:22

这个系列已经写了 3 篇了。这篇写如何使用 iframe 解决标题里面提到的问题。

前情提要

请看上一篇博文:

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

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

缘由

缘由仍然是我想在网页里面放一个富文本编辑器 Quill Editor,然后又想用 Pico.css 给网页一个好看的格式。

然后,Quill Editor 的工具栏按钮的样式被 Pico.css 污染了。搞得很不好看。但是,页面里面还有其它的元素,需要使用 Pico.CSS 来给样式。

前面有篇文章,我提到用 iframe 的方式,把 Quill Editor 单独放在一个没有 Pico.css 的页面中,然后把这个页面,采用 iframe 的方式嵌入到有 Pico.css 的框架页面中。

当时的问题和解决方案

当时想到采用 iframe,测试确实屏蔽了框架页面的 CSS,但是,和 Editor 在一起的,还有其它的输入框。这些输入框,需要给 Pico.css 给以样式。否则这些输入框会很难看。

然后想到采用 Shadow DOM 的解决方案,而不是采用 iframe 的方式。没成想,测试下来,发现 Shadow DOM 的方式,让 Quill Editor 的原本的功能都无法正常操作了。

兜兜转转一大圈,回到 iframe 这个方案

使用 iframe 这个方案,同时要解决页面上其它的页面组件,比如文章标题的输入框,要受到 Pico.css 样式的影响,提交用的按钮,要受到 Pico.css 样式的影响,就必须把文章标题输入框,提交按钮,等,放到 iframe 外面,也就是放到框架页面上。

接下来的问题就是,如何在提交的时候,能同时提交标题输入框的内容,和 iframe 里面的富文本编辑器里面用户输入的内容。

当然,如果写很复杂的 JavaScript 代码,肯定是能搞定这个问题的。但我想要越简单越好,尽量少写代码。比如,我不会自己去创建一个 XMLHttpRequest 用来向服务器提交。我也不想要引入 jQuery 库或者其它的 AJAX 库来干这个事情。因为这样会给页面引入更多的东西,用户打开页面需要下载更多的数据,消耗更多带宽和更多的等待时间。

所以,我还是想用页面的 <form> 来直接提交。

因此,问题就转化成:我在框架页面里面的标题输入框,放进一个 <form> 表单,如何能把这个在 iframe 里面的 Editor 的内容也放进这个 <form> 表单一起提交?

办法就是,用 JavaScript 从框架页面里,把 iframe 页面里的富文本编辑器的内容,读取出来,放到 <form> 表单内部的一个隐藏字段,然后提交表单。这样提交表单时,就可以把标题和文章内容一起提交了。

因此,问题就转化为:如何从框架页面里面,读到 iframe 里面的东西?或者说,如何调用到 iframe 里面的页面的 JavaScript 函数。

解决上述问题的代码如下:

var a = document.querySelector("iframe");
var MyRichText = a.contentWindow.GetContent();

解释一下:

上述代码里面,GetContent() 函数是 Quill Editor 页面里面写的函数。这个函数从 Quill Editor 里面读出用户输入的富文本文字内容。

首先,通过 querySelector("iframe") 把 iframe 找到,然后,取它的 window,然后就是调用 JS 函数。

当然,这里取到 iframe 对象,然后取到它里面的 window 对象,也就可以取到底下的 document 对象,顺理成章就能取到 document 对象底下的按钮,输入框等等。

解决了这个问题,剩下的问题就简单了。用户点击提交按钮,从 iframe 里面读到富文本编辑器的内容后,把内容放到一个隐藏不显示的位于表单内的一个输入框里面,然后提交表单。

到此搞定,测试通过。

完整代码

富文本编辑器所在页面的代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Quill Form Submission</title>
    <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">
</head>
<body>
    <!-- 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="editor" class="edit_container">
    </div>



      <script>
          console.log("开始初始化 quill editor");

            const quill = new Quill("#editor", {
                modules: {
                toolbar: [
          { header: [1, 2, 3, 4, 5, 6, false] }, // 标题
          'bold',             // 加粗
          'italic',           // 斜体
          'blockquote',       // 引用
          'link',             // 超链接
          'image',            // 插入图片
          'video',            // 插入视频
          'code',             // 行内代码
          'code-block',       // 代码块
          { list: 'bullet' }, // 无序列表
          { list: 'ordered'}, // 有序列表
          'strike',           // 删除线
          { 'align': [] },    // 对齐方式
          'formula'           // 公式
        ]
                },
                theme: 'snow'
            });


            function GetContent(){
              const html = quill.root.innerHTML;
              //document.getElementById('htmlInput').value = html;
              var AContent = html;
              return AContent;
            };
      </script>
  </body>
</html>

框架页面的代码

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title></title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css"
    />
  </head>

  <body>
      <main class="container">
      这里是 Frame 的框架页面。里面是一个 iFrame 页面。

          <form id="myForm" action="ContentUpdate" method="post">
            <input type="hidden" name="html" id="htmlInput">
            <div><label for="MySubject1">标题<input type="text" value="这是标题" name="MySubject1" id="MySubject1" /></label></div>
          </form>

          <iframe id="MyEditor" src="http://localhost:8080/GetEditor" width="100%" height="550"  frameborder="no"></iframe>

          <div>
            <input type="submit" value="Submit" onclick="SubmitRichEditor()" />
          </div>
      </main>

      <script>
        function SubmitRichEditor(){
          var a = document.querySelector("iframe");
          var b = a.contentWindow.document;

          var c='';
          c = a.contentWindow.GetContent();

          console.log("开始从 quill 富文本编辑器读取内容");
          var hi = document.getElementById('htmlInput');
          hi.value = c;
          console.log(hi.value);

          console.log("开始提交");
          var AForm = document.getElementById('myForm');
          AForm.submit();

        };
      </script>
  </body>
</html>

页面图片:

结论

对于页面组件的封装,比如这个富文本编辑器,采用 iframe 看起来是一个比较简单好用的方式。至少比使用 Shadow DOM 的问题少。

当然,假如页面上有一大堆组件,每个都放进 iframe 的话,对浏览器的资源消耗会比较大,可能并不合适。

但是,单纯针对富文本编辑器来看,采用 iframe 来封装,应该是最佳方案。凡是需要它的页面,直接插入一个 iframe 就搞定,直接引入的是一个单独的页面。这个单独的页面,就可以作为一个组件,重复使用,实现代码重用。也让原本可能很复杂的页面简化,把富文本编辑器部分的代码抽离出来成为一个单独的文件,方便修改维护。当然,同时也实现了对 css 样式的封装和隔离,避免了 css 污染的问题。

one more thing...

为了解决这个富文本编辑器被 css 污染的问题,我还测试了7,8 个其它的开源免费的富文本编辑器,有一些不太好用,有一些同样会受到 css 污染,有一些功能又不太够,有一些入门的学习曲线比较陡峭,很难简单地创建一个页面就把它用起来。等等。但是,发现一个国产的富文本编辑器 wangEditor 很不错,功能强大,也很容易使用。它的工具栏样式也不受 Pico.css 的影响 --- 其实也受了点影响,工具栏按钮上的文字变大了。工具栏按钮文字变大后,看起来没那么好看美观,但不严重,不影响使用。而且,wangEditor 的在线文档也非常详细。必须赞一个。

那么,读到这篇文章的同学,也给这篇文章点个赞吧。