如何限制粘贴在可编辑区域中的文本样式?

Mik*_*els 6 html javascript css regex contenteditable

我遇到了这篇 Stack Overflow 帖子,其中讨论了我所需要的确切内容:能够将文本粘贴到内容可编辑的区域,只保留一些样式。我在那里运行了代码片段,它工作正常。但是,当我在我的页面上尝试时,所有样式都被删除了,包括我想保留的样式,例如粗体和斜体。在比较代码和一些实验后,我意识到它不起作用的原因是因为我使用的是外部 CSS,而不是内联。

有什么办法可以使它与外部 CSS 一起使用吗?我永远不会知道用户将在该 contenteditable 中发布的文本的来源,以及如何对其应用样式,因此我希望解决所有可能性。

另外,有没有办法让它与拖放的文本一起工作,而不仅仅是粘贴的文本?我尝试将它正在侦听的事件从“粘贴”替换为“丢弃”,但出现错误e.clipboardData is undefined

const el = document.querySelector('p');

el.addEventListener('paste', (e) => {
  // Get user's pasted data
  let data = e.clipboardData.getData('text/html') ||
      e.clipboardData.getData('text/plain');
  
  // Filter out everything except simple text and allowable HTML elements
  let regex = /<(?!(\/\s*)?(b|i|em|strong|u)[>,\s])([^>])*>/g;
  data = data.replace(regex, '');
  
  // Insert the filtered content
  document.execCommand('insertHTML', false, data);

  // Prevent the standard paste behavior
  e.preventDefault();
});
Run Code Online (Sandbox Code Playgroud)
.editable {
  width: 100%;
  min-height: 20px;
  font-size: 14px;
  color: black;
  font-family: arial;
  line-height: 1.5;
  border: solid 1px black;
  margin-bottom: 30px;
  }
  
.big {
  font-size: 20px;
}

.red {
  color: red;
}

.bold {
  font-weight: bold;
}

.italic {
  text-decoration: italic;
}
Run Code Online (Sandbox Code Playgroud)
<p class="editable" contenteditable></p>

<p class="notEditable">
  Try pasting this paragraph into the contenteditable paragraph above. This text includes <b>BOLD</b>, <i>ITALIC</i>, <s>STRIKE</s>, <u>UNDERLINE</u>, a <a href='#'>LINK</a>, and <span style="font-size:30px; color:red; font-family:Times New Roman">a few other styles.</span> All styles are inline, and it works as expected.
</p>

<p>Now, try pasting this paragraph with external styles. <span class="big">Big</span > <span class="red">red</span> <span class="bold">bold</span> <span class="italic">italic</span>. It no longer works.</p> 
Run Code Online (Sandbox Code Playgroud)

小智 1

不幸的是,没有办法从外部源保留类的属性。如果您要打印剪贴板的内容,您将看到收到外部页面上的原始 HTML 内容,例如:

<div class="some-class">this is the text</div>
Run Code Online (Sandbox Code Playgroud)

浏览器不会内联类属性!由于内容来自外部来源,因此您无法控制它。

另一方面,如果内容来自您的页面(因此定义了类),您可以解析接收到的 HTML 并过滤 CSS 属性,仅保留您想要的内容。这里有一个使用普通 Javascript 的代码示例,不需要任何库(也可以在Codepen上找到):

<div class="some-class">this is the text</div>
Run Code Online (Sandbox Code Playgroud)
const targetEditable = document.querySelector('p');

targetEditable.addEventListener('paste', (event) => {
    let data = event.clipboardData.getData('text/html') ||
        event.clipboardData.getData('text/plain');

    // Filter the string using your already existing rules
    // But allow <p> and <div>
    let regex = /<(?!(\/\s*)?(div|b|i|em|strong|u|p)[>,\s])([^>])*>/g;
    data = data.replace(regex, '');

    const newElement = createElementFromHTMLString(data);
    const cssContent = generateFilteredCSS(newElement);
    addCssToDocument(cssContent);

    document.execCommand('insertHTML', false, newElement.innerHTML);
    event.preventDefault();
});

// Scan the HTML elements recursively and generate CSS classes containing only the allowed properties
function generateFilteredCSS(node) {
    const newClassName = randomString(5);
    let content = `.${newClassName}{\n`;

    if (node.className !== undefined && node.className !== '') {
        // Get an element that has the class
        const elemOfClass = document.getElementsByClassName(node.className)[0];
        // Get the computed style properties
        const styles = window.getComputedStyle(elemOfClass);

        // Properties whitelist, keep only those
        const propertiesToKeep = ['font-weight'];
        for (const property of propertiesToKeep) {
            content += `${property}: ${styles.getPropertyValue(property)};\n`;
        }
    }
    content += '}\n';
    node.className = newClassName;

    for (const child of node.childNodes) {
        content += generateFilteredCSS(child);
    }

    return content;
}

function createElementFromHTMLString(htmlString) {
    var div = document.createElement('div');
    div.innerHTML = htmlString.trim();
    return div;
}

function addCssToDocument(cssContent) {
    var element = document.createElement("style");
    element.innerHTML = cssContent;
    var header = document.getElementsByTagName("HEAD")[0];
    header.appendChild(element);
}

function randomString(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for (var i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)
.editable {
  width: 100%;
  min-height: 20px;
  font-size: 14px;
  color: black;
  font-family: arial;
  line-height: 1.5;
  border: solid 1px black;
  margin-bottom: 30px;
  }
  
.red-bg {
  background-color: red;
  font-weight: bold;
}
Run Code Online (Sandbox Code Playgroud)

关于拖放功能,你必须event.dataTransfer.getData()drop事件监听器中使用,其余的都是一样的。

参考

  1. 如何从 HTML 字符串生成 DOM 元素
  2. 如何使用 Javascript 在运行时添加 CSS 类
  3. 如何在 Javascript 中生成随机字符串(唯一 ID)
  4. 拖放数据传输