道招

Javascript去除内容格式并匹配目标格式粘贴

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

Javascript去除内容格式并匹配目标格式粘贴

先说下需求:

  1. 支持将复制的文本内容粘贴(包括覆盖)contenteditable为true的目标div里面
  2. 复制内容的文本如果有多行,粘贴后需要保留它们直接的换行效果
  3. 如果覆盖的目标div有多行文字,粘贴内容需要匹配第一处文字的格式(样式都是以内联形式写入style中)

比如我们想将复制的文字,覆盖粘贴下面的目标div中,文字保留换行后,格式(字体、字号、颜色等)应该跟第一行加粗的保持一致

下面我们举例说明 我们需要复制的文字 复制源 我们准备覆盖粘贴的目标

粘贴目标

很显然,需求要的效果是这三行文字保留换行效果,并且格式跟“伙伴”一致,即也是strong效果。 应该是如下:

下面看看这个需求的解决思路

我们很自然的会想到下面两个场景

  1. 不干预,直接用浏览器默认行为
  2. 使用document.execCommand('insertHtml', false, '粘贴内容')的方式
不干预效果

图片

内容是进来了,但是保留了复制源的格式,不符合需求。

document.execCommand('insertHtml', false, '粘贴内容')效果

图片

没有复制源的格式了,但是粘贴后格式不匹配,不是想要的strong效果,并且字体还变小了。

看来我们想偷懒,利用浏览器自带功能是不行了,我们继续拆解问题。 复制的文字需要分成‘三段’,每一段之间有换行,其中这里面就有很大一个难点,怎么换行?

在图中我们看到的换行,其实是由于有换行符的效果,我们肉眼看不到它而已。 如果你的需求是一定要保留这个换行效果,那我们只能使用<br />了。

const getPastePlainText = (win, e) => {
  const clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData;
  let pasteText = '';
  if (!clipboardData) {
    pasteText = win.clipboardData && win.clipboardData.getData('text');
  } else {
    pasteText = clipboardData.getData('text/plain');
  }
  // 换行转为br
  return pasteText.replace(/(\r\n|\r|\n)/g, '<br/>');
};

使用了<br />这样就会带来一个新的问题,本来只要想办法处理纯文字就行了,现在必须要加入html标签了,那么添加/替换文字的方式只能使用innerHTML了,不然加入的<br />就会有以文本形式显示出来了,这样我们就需要获取待粘贴文字的父级node了,通过它来获取目标div需要的样式。这样我们就把待粘贴的文本及其目标格式组装好了。

替换的思路是先删除不要的(比如被选中的),再插入需要加进去的,删除的工作可以交给window.getSelection().deleteFromDocument(),它可是帮了大忙啊。

代码如下

const inlineTags = ['span', 'a', 'br', 'b', 'strong', 'img', 'sup', 'sub', 'i', 'em', 'del', 'u', 'input', 'textarea', 'select'];
const plainText = getPastePlainText(window, e);
const range = window.getSelection().getRangeAt(0);
const { anchorNode } = window.getSelection();
const parentNode = anchorNode.nodeType === 3 ? anchorNode.parentNode : anchorNode; // anchorNode为textNode,其父级才算包裹的有效节点
const grandParentNode = parentNode.parentNode;
const parentTagName = parentNode.tagName.toLowerCase();
// 如果原来是内联元素则继续使用原标签,否则使用span
const tagName = inlineTags.includes(parentTagName) ? parentTagName : 'span';
let parentStyle = parentNode.getAttribute('style');
const finalText = `<${tagName} ${parentStyle ? `style="${parentStyle}"` : ''}>${plainText}</${tagName}>`;
window.getSelection().deleteFromDocument();
const el = document.createElement('div');
el.innerHTML = finalText;
const frag = document.createDocumentFragment();
const childNode = el.firstChild;
const blank = document.createTextNode(''); // 利用一个空白的textNode方便鼠标定位在最后面和用户连续粘贴
childNode.appendChild(blank);
frag.appendChild(childNode);
range.insertNode(frag);
const cursorRange = document.createRange();
cursorRange.selectNodeContents(blank); // 让光标聚焦在最后面那个空白textNode
window.getSelection().removeAllRanges();
window.getSelection().addRange(cursorRange);
更新时间:
上一篇:vue内置组件keep-alive源码解析下一篇:WordPress项目做vue的后台管理系统

相关文章

Javascript保留格式翻译选区内容及预览(一)

目前市面上的不少翻译,一般场景比较简单,都是纯文本翻译(可能会包含换行\n之类的),但是最近遇到一个需求是要实现富文本里面的翻译,这里的翻译很大的概率会有格式,比如这种 我们需要带格式翻 阅读更多…

关注道招网公众帐号
道招开发者二群