【问题标题】:Quill link handler not working羽毛笔链接处理程序不起作用
【发布时间】:2021-10-22 02:20:50
【问题描述】:

我正在尝试为链接输入值编写自定义处理程序。如果用户输入的链接没有自定义协议,我希望在输入值之前添加 http:。这是因为如果链接值缺少 http:,则不会解释链接,而是会显示 about:blank。 (https://github.com/quilljs/quill/issues/1268#issuecomment-272959998)

这是我写的(类似于官方示例here):

toolbar.addHandler("link", function sanitizeLinkInput(linkValueInput) {
    console.log(linkValueInput); // debugging

    if (linkValueInput === "")
        this.quill.format(false);

    // do nothing, since it implies user has just clicked the icon
    // for link, hasn't entered url yet
    else if (linkValueInput == true);

    // do nothing, since this implies user's already using a custom protocol
    else if (/^\w+:/.test(linkValueInput));

    else if (!/^https?:/.test(linkValueInput)) {
        linkValueInput = "http:" + linkValueInput;
        this.quill.format("link", linkValueInput);
    }
});

每次用户单击链接图标时,什么都不会发生,true 会记录到控制台。我实际上希望当人们在按下链接图标后显示的工具提示上单击“保存”时执行此处理程序。

知道怎么做吗?提示或建议也值得赞赏。

谢谢!

【问题讨论】:

    标签: javascript quill


    【解决方案1】:

    汇总所有信息

    雪主题本身使用工具栏的 addHandler 来显示工具提示,因此无法使用 addHandler 方法来实现我们想要的。

    所以,我们可以这样做:

    var Link = Quill.import('formats/link');
    var builtInFunc = Link.sanitize;
    Link.sanitize = function customSanitizeLinkInput(linkValueInput) {
        var val = linkValueInput;
    
        // do nothing, since this implies user's already using a custom protocol
        if (/^\w+:/.test(val));
        else if (!/^https?:/.test(val))
            val = "http:" + val;
    
        return builtInFunc.call(this, val); // retain the built-in logic
    };
    

    此方法不挂钩处理程序,而是修改内置的清理逻辑本身。我们还保留了清理的原始行为,因此不会修改编辑器的原始行为。

    或者,我们实际上可以使用this code 挂钩到工具提示的保存按钮。但是与上面的方法相比,它的方法太长了。

    【讨论】:

      【解决方案2】:

      据我所知,创建和更新链接的处理在 Quill 的源代码中有点分散。默认的 Snow 主题在一定程度上处理编辑链接:它在内部跟踪用户选择和最后选择的链接。因此,我认为仅使用自定义处理程序不可能在 Quill 中实现您目前想要的。

      您可能想打开一个问题来报告此问题,作者可能愿意添加这样的处理程序。

      与此同时,我想出了一种方法来更新链接,只需侦听导致编辑工具提示关闭的事件。有一些复杂性,因为可以编辑链接,然后主题依靠其内部跟踪来更新它。但是,总而言之,我认为这个解决方案还不错。您可能想在这里和那里添加一些错误检查,但总体而言,它似乎工作得很好并且可以做您想做的事情。我创建了一个Fiddle 来演示这一点。为了完整起见,我也将其作为代码 sn-p 包含在此处。

      var quill = new Quill('#editor', {
          modules: {
            toolbar: true
          },
          theme: 'snow'
        }),
        editor = document.getElementById('editor'),
        lastLinkRange = null;
      
      /**
       * Add protocol to link if it is missing. Considers the current selection in Quill.
       */
      function updateLink() {
        var selection = quill.getSelection(),
          selectionChanged = false;
        if (selection === null) {
          var tooltip = quill.theme.tooltip;
          if (tooltip.hasOwnProperty('linkRange')) {
            // user started to edit a link
            lastLinkRange = tooltip.linkRange;
            return;
          } else {
            // user finished editing a link
            var format = quill.getFormat(lastLinkRange),
              link = format.link;
            quill.setSelection(lastLinkRange.index, lastLinkRange.length, 'silent');
            selectionChanged = true;
          }
        } else {
          var format = quill.getFormat();
          if (!format.hasOwnProperty('link')) {
            return; // not a link after all
          }
          var link = format.link;
        }
        // add protocol if not there yet
        if (!/^https?:/.test(link)) {
          link = 'http:' + link;
          quill.format('link', link);
          // reset selection if we changed it
          if (selectionChanged) {
            if (selection === null) {
              quill.setSelection(selection, 0, 'silent');
            } else {
              quill.setSelection(selection.index, selection.length, 'silent');
            }
          }
        }
      }
      
      // listen for clicking 'save' button
      editor.addEventListener('click', function(event) {
        // only respond to clicks on link save action
        if (event.target === editor.querySelector('.ql-tooltip[data-mode="link"] .ql-action')) {
          updateLink();
        }
      });
      
      // listen for 'enter' button to save URL
      editor.addEventListener('keydown', function(event) {
        // only respond to clicks on link save action
        var key = (event.which || event.keyCode);
        if (key === 13 && event.target === editor.querySelector('.ql-tooltip[data-mode="link"] input')) {
          updateLink();
        }
      });
      <link href="https://cdn.quilljs.com/1.1.10/quill.snow.css" rel="stylesheet" />
      <script src="https://cdn.quilljs.com/1.1.10/quill.min.js"></script>
      <div id="editor"></div>

      如果您有任何问题,请告诉我。

      【讨论】:

      • 谢谢!您的代码运行良好! > “有一些复杂性,因为可以编辑链接,然后主题依靠其内部跟踪来更新它。”您的代码似乎也确实适用于链接的编辑。我不明白这是个什么问题。
      • 好吧,我必须做一些额外的编码才能使它工作,这就是我的意思:-)
      【解决方案3】:

      工具栏处理程序只是在单击工具栏中的按钮时调用您给定的函数。传入的value 取决于用户选择中该格式的状态。因此,如果用户仅突出显示纯文本并单击链接按钮,您将获得false。如果用户突出显示链接,您将获得链接的值,默认为 url。这里用一个例子来解释:http://quilljs.com/docs/modules/toolbar/#handlers

      snow 主题使用工具栏的 addHandler 本身来显示工具提示,看起来您正在尝试挂钩,目前这是不可能的。

      看起来您真正想做的是控制链接的清理逻辑。清理存在于 Quill 中的较低级别,因为有许多方法可以插入链接,例如从工具提示 UI、粘贴、不同的 API 等等。因此,要涵盖它们,所有逻辑都在链接格式本身中。 http://quilljs.com/guides/cloning-medium-with-parchment/#links 中专门介绍了自定义链接格式的示例。您也可以只修改 Quill 自己的 sanitize 方法,但不建议这样做,因为 semver 没有记录或涵盖它。

      let Link = Quill.import('formats/link');
      Link.sanitize = function(value) {
        return 'customsanitizedvalue';
      }
      

      【讨论】:

      • 链接羊皮纸方法看起来很简单。我只是看看如何集成这些 LinkBlot 类并在一两天内回来。
      • 我刚刚查看了羊皮纸示例,似乎我们无法“查看”其中的当前链接值(正如我们可以通过单击现有链接在通常的羽毛笔编辑器中执行的那样)
      • 我得到了最后一个方法(修改sanitize 方法)来工作。 最后一个问题: 我想做这样的事情:Link.sanitize = function(value) { return originalSanitizeMethod(customSanitize(value)); }i.e.首先使用我的逻辑进行清理,然后将新值发送回通常的内置函数。所以,我做了:return previouslyStoredBuiltInSanitize.call(this, customVal); 在我修改后的方法中;你的应用可以吗?我实际上已经完成了这项工作,但我担心它在您的应用程序中可能存在的任何缺点。
      • 它可以在当前版本中使用是的。这有点不必要的限制,因为您只需要返回一个字符串,而不管如何。我可能会继承并调用 super.sanitize 而不是自己保存引用,但这可能只是偏好。
      【解决方案4】:

      半小时后

      找到这个解决方案

      htmlEditorModuleConfig = {
          toolbar: [
              ['link']                        
          ],    
              bounds: document.body   
      }
      

      在配置中添加'bounds: document.body'

      【讨论】:

        【解决方案5】:

        我必须做同样的事情,(在发送到服务器之前验证 url)所以我最终得到了这样的东西。

         const editor = new DOMParser().parseFromString(value, 
        'text/html');
        const body = qlEditor.getElementsByTagName('body');    
        const data = document.createElement('div');
        data.innerHTML = body[0].innerHTML;
        Array.from(data.querySelectorAll('a')).forEach((ele) => {
          let href  = ele.getAttribute('href');
          if (!href.includes('http') && !href.includes('https')) {
            href = `https://${href}`;
            ele.setAttribute('href', href);
          }
        });
        body[0].innerHTML = data.innerHTML;
        

        【讨论】:

          【解决方案6】:

          也许这是一个老问题,但这是我让它发挥作用的方式。

          首先,它将其他自定义协议列入白名单以接受为有效协议。 然后,我们运行 Quill 核心中已经包含的 sanitize 方法,并根据自定义协议列表返回 URL 或 about:blank。 那么,如果这是一个about:blank 是因为它没有通过清理方法。如果我们得到 URL,那么我们验证它是否具有列表中的协议,如果没有,那么我们附加 http://,这样我们就不会得到相对 URL 或空白,除非它没有被列入白名单:

          • https://your-site.com/www.apple.com
          • about:blank

          希望它能帮助遇到同样问题的其他人。

          const Link = Quill.import('formats/link')
          // Override the existing property on the Quill global object and add custom protocols
          Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel', 'radar', 'rdar', 'smb', 'sms']
          
          class CustomLinkSanitizer extends Link {
            static sanitize(url) {
              // Run default sanitize method from Quill
              const sanitizedUrl = super.sanitize(url)
          
              // Not whitelisted URL based on protocol so, let's return `blank`
              if (!sanitizedUrl || sanitizedUrl === 'about:blank') return sanitizedUrl
          
              // Verify if the URL already have a whitelisted protocol
              const hasWhitelistedProtocol = this.PROTOCOL_WHITELIST.some(function(protocol) {
                return sanitizedUrl.startsWith(protocol)
              })
          
              if (hasWhitelistedProtocol) return sanitizedUrl
          
              // if not, then append only 'http' to not to be a relative URL
              return `http://${sanitizedUrl}`
            }
          }
          
          Quill.register(CustomLinkSanitizer, true)
          

          【讨论】:

            猜你喜欢
            • 2018-08-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-10
            • 1970-01-01
            • 1970-01-01
            • 2016-09-22
            • 1970-01-01
            相关资源
            最近更新 更多