这个事情怎么说呢,我前不久弄了个 Markdown 邮件在线编辑器,可是有个小伙伴给我提了一个需求:能不能再把邮件转回 Markdown 呢?于是我就一直想完善这一功能,想要实现这一功能首先我们得先获取到粘贴的 html 吧,但是我试了试直接粘贴到 textarea 里面就会变成纯文本的,咦,这就很神奇了。

那么 QQ 邮箱是怎么实现的呢?通过多番打探,终于发现这个神奇的玩意 textarea 的粘贴事件,通过这东西我们就能获取到 html 格式的粘贴文本,我们瞧瞧代码

 var editableDiv = document.getElementById('textarea_id');
  // 获取编辑框对象

  function handlepaste (e) {
      var types, pastedData, savedContent;

      // Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
      if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {

        // 在类型列表中检查 "text/html"
        // 需要 DOMStringList 位,我们不需要处理 'text/plain' 的内容,直接返回就好了
        // Safari/Edge 也是直接返回就好了
        types = e.clipboardData.types;
        if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) {

          // 提取数据并将其传递给回调
          pastedData = e.clipboardData.getData('text/html');
          processPaste(editableDiv, pastedData);

          // 停止实际粘贴数据
          e.stopPropagation();
          e.preventDefault();
          return false;
        }
      }

      // 将现有元素内容移动到 DocumentFragment 以便保存数据
      savedContent = document.createDocumentFragment();
      while(editableDiv.childNodes.length > 0) {
          savedContent.appendChild(editableDiv.childNodes[0]);
      }

      // 等待浏览器将内容粘贴到其中并进行清理
      waitForPastedData(editableDiv, savedContent);
      return true;
  }

  function waitForPastedData (elem, savedContent) {

      // 数据已由浏览器处理,对其进行处理
      if (elem.childNodes && elem.childNodes.length > 0) {

          // 通过 innerHTML 检索粘贴的内容
          // 或者在这里循环遍历 elem.childNodes 或 elem.getElementsByTagName
          var pastedData = elem.innerHTML;

          // 恢复保存的内容
          elem.innerHTML = "";
          elem.appendChild(savedContent);

          // 跳转返回数据
          processPaste(elem, pastedData);
      }

      // 等待20毫秒再试一次
      else {
          setTimeout(function () {
              waitForPastedData(elem, savedContent)
          }, 20);
      }
  }

  function processPaste (elem, pastedData) {
      // 取到粘贴的 Html 数据为 pastedData
      console.log(pastedData);
  }

  if (editableDiv.addEventListener) {
    editableDiv.addEventListener('paste', handlepaste, false);
    // 现代浏览器,注意:Firefox <= 6 需要第3个参数
  } else {
    editableDiv.attachEvent('onpaste', handlepaste);
    // IE <= 8
  }

简单介绍

processpaste processpaste 事件附加了 types 函数并传递了一个参数:粘贴事件的 DOMStringList 对象。 我们特别感兴趣的是此事件的 contains 属性,它允许在非浏览器中进行剪贴板访问。 在IE中,等效的是 indexOf 。尽管这有一个稍微不同的API,请参阅下面的资源部分

processpaste 这个功能有两个分支,首先检查是否存在 processpaste 并检查它的 types 属性是否包含'text / html'( types 可以是使用 contains 方法检查的 DOMStringList ,也可以是使用 indexOf 方法检查的字符串)。 如果满足所有这些条件,那么我们按照解决方案继续,除了 'text / html' 而不是 'text / plain' 。 目前适用于 Chrome 和 Firefox 22+ 。

如果不支持此方法的其他浏览器 ,那么我们可以将元素的内容保存到 processpaste 或者清空元素

waitForPastedData 此函数首先轮询粘贴的数据(每20ms一次),这是必要的,因为它不会立即显示。 当数据出现时:先将可 textarea(现在是粘贴数据)的 innerHTML 保存到变量中,然后恢复 DocumentFragment 中保存的内容再使用检索到的数据调用 ' processPaste ' 函数

processPaste 方法使用粘贴的数据做任意事情。 在这种情况下,我只是打印数据,你可以做任何你喜欢的事情。 您可能希望通过某种数据清理过程运行粘贴的数据。

保存并恢复光标位置,在实际情况下,你可能希望在之前保存选择,然后将其恢复(在 textarea 上设置光标位置)。 然后,你可以在用户启动粘贴操作时将粘贴的数据插入光标所在的位置