为了账号安全,请及时绑定邮箱和手机立即绑定

不可编辑的内容允许删除。如何限制这个?

不可编辑的内容允许删除。如何限制这个?

江户川乱折腾 2023-05-19 16:15:30
我们使用了带有“不可编辑”插件的 TinyMCE 编辑器。我们尝试删除不可编辑的内容,它被删除了。如何限制不可编辑内容的删除(删除/退格)操作?下面是我的代码:tinymce.init({  selector: "#myeditablediv",  plugins: "advlist table lists image paste link pagebreak noneditable help",  noneditable_noneditable_class: "mceNonEditable",  menubar: false,  inline: true,  height: 500,  paste_data_images: true,  toolbar_sticky: true,  toolbar:    "bold italic underline | superscript subscript | formatselect | bullist | code pagebreak | link image | COC | table | removeformat | help",  formats: {    editable: {      inline: "span",      styles: { borderBottom: "2px solid gray" },      classes: "mceEditable"    }  },  setup: function (editor) {    editor.ui.registry.addButton("COC", {      text: "<b style='font-size:large;font-weight:bold;'>{CC}</b>",      tooltip: "CopyToClipBoard",      onAction: function (api) {        editor.execCommand("Copy");      }    });  },  toolbar_mode: "floating"});<script src="https://cdn.tiny.cloud/1/qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc/tinymce/5/tinymce.min.js"></script><div class="demo-inline">  <div id="myeditablediv">    Hi tiny    <p class='mceNonEditable'> <b> This is a non editable content</b>    </p>    <p> <span class='mceNonEditable'> <b>This part is non editable</b> </span>      This is a editable content      <span class='mceNonEditable'> <b>This part is non editable</b> </span>    </p>  </div></div>
查看完整描述

2 回答

?
哔哔one

TA贡献1854条经验 获得超8个赞

TinyMCE 的noneditable插件旨在使内容块不可编辑,但不可删除。相反,它将不可编辑内容的整个部分视为单个字符。

要阻止内容被键盘删除,您可以使用 Tiny 的事件处理结构来查找某些按键,然后中断/停止它们。

您需要展开它以查看光标在内容中的位置,如果按键的结果会删除您想要保留的内容,则仅在这些情况下停止按键。

请注意,此方法不会阻止通过其他方法删除内容,例如将其作为更大选择的一部分删除。


查看完整回答
反对 回复 2023-05-19
?
温温酱

TA贡献1752条经验 获得超4个赞

编写了一个到目前为止运行良好的 Angular 服务,可能需要针对边缘情况进行一些调整。nonDeletableSelectors包含表示应该不可删除的元素的 CSS 选择器。我注意到显然有一个带有不可编辑元素的 TinyMCE 错误,所以代码比我想象的更复杂。

import {Injectable} from '@angular/core';


@Injectable({

  providedIn: 'root'

})

export class EditorPreventDeleteService {


  constructor() { }


  public nonDeletableSelectors = ['.mceNonEditable'];


  public preventDelete(editor) {

    let self = this;

    editor.on('keydown', function(event) {

      if (self.keyWillDelete(event)) {

        let range = editor.selection.getRng(), selection = editor.selection.getSel();

        if (!range.collapsed)

          return self.checkSelection(editor, event);

        else if (event.keyCode == 8)

          self.checkBackspace(editor, event, selection);

        else if (event.keyCode == 46)

          self.checkDelete(editor, event, selection);

      }

      return true;

    });

    editor.on('beforeSetContent', event => {

      return self.checkSelection(editor, event);

    });

    editor.on('dragstart', event => {

      if (self.checkNode(event.target, true))

        self.cancelEvent(event);

    });

  }


  protected checkNode(node, includeChildren = true) {

    if (node && node.nodeType !== Node.TEXT_NODE)

      for (let nonDeletableSelector of this.nonDeletableSelectors)

        if (node.matches(nonDeletableSelector)

            || (includeChildren && node.querySelectorAll(nonDeletableSelector).length > 0))

          return true;

    return false;

  }


  protected checkSelection(editor, event) {

    const selectedHTMLString = editor.selection.getContent({format : 'html'});

    const selectedHTML = new DOMParser().parseFromString(selectedHTMLString, 'text/html').documentElement;

    if (this.checkNode(selectedHTML))

      return this.cancelEvent(event);

    return true;

  }


  protected checkBackspace(editor, event, selection) {

    if (selection.anchorOffset === 0 && this.getPrefixContent(editor, selection).length === 0)

      return this.cancelEvent(event);

    this.checkCaretDeletion(editor, event, selection, false);

  }


  protected checkDelete(editor, event, selection) {

    this.checkCaretDeletion(editor, event, selection, true);

  }


  protected checkCaretDeletion(editor, event, selection, forwards = true) { // https://developer.mozilla.org/en-US/docs/Web/API/Selection

    let borderingElement = forwards ? selection.anchorNode.nextSibling : selection.anchorNode.previousSibling;

    if (selection.anchorNode.nodeType === Node.TEXT_NODE) {

      if (this.getTrailingText(selection, forwards, false).length > 0)

        return; // not at the border of a text element

    } else if (selection.anchorOffset !== (forwards ? selection.anchorNode.childNodes.length : 0)

        && this.trimZeroWidthSpaces(selection.anchorNode.textContent).length > 0

        && this.checkNode(selection.anchorNode.childNodes.item(selection.anchorOffset + (forwards?0:1))))

        return this.cancelEvent(event); // not at the border of anchor, anchor not empty, only neighbouring child is deleted

    if (this.checkNode(selection.anchorNode) || this.checkNode(borderingElement))

      this.cancelEvent(event);

  }


  protected getPrefixContent(editor, selection) {

    let currentRange = editor.selection.getRng(1), tempRange = currentRange.cloneRange();

    tempRange.setStartBefore(editor.getBody().childNodes.item(0));

    tempRange.setEndBefore(selection.anchorNode);

    editor.selection.setRng(tempRange);

    let content = editor.selection.getContent({format: 'html'});

    editor.selection.setRng(currentRange);

    return this.trimZeroWidthSpaces(content.trim());

  }


  protected getTrailingText(selection, forwards = true, includeSiblings = false) {

    let trailer = '', appendTrailer = function(text) { forwards ? trailer += text : trailer = text + trailer; }

    if (selection.anchorNode.nodeType === Node.TEXT_NODE) {

      let text = selection.anchorNode.textContent;

      appendTrailer(forwards ? text.substr(selection.anchorOffset) : text.substr(0, selection.anchorOffset));

    } else {

      for (let i=selection.anchorOffset ; i>=0 && i<selection.anchorNode.childNodes.length ; i+=(forwards?-1:1))

        appendTrailer(selection.anchorNode.childNodes.item(i).textContent);

    }

    if (includeSiblings) {

      let sibling = selection.anchorNode.previousSibling;

      while (sibling) {

        appendTrailer(sibling.textContent);

        sibling = sibling.previousSibling;

      }

    }

    return this.trimZeroWidthSpaces(trailer);

  }


  protected cancelEvent(event) {

    event.preventDefault();

    event.stopPropagation();

    return false;

  }


  protected keyWillDelete(evt) {

    let c = evt.keyCode;

    if (evt.ctrlKey)

      return evt.key == 'x' || [8, 46].includes(c);

    return [8, 9, 13, 46].includes(c)

        || this.inRange(c, 48, 57)

        || this.inRange(c, 65, 90)

        || this.inRange(c, 96, 111)

        || this.inRange(c, 186, 192)

        || this.inRange(c, 219, 222);

  }


  protected inRange(val, min, max) {

    return val >= min && val <= max;

  }


  protected trimZeroWidthSpaces(text: string) {

    return text.replace(/[\u200B-\u200D\uFEFF]/g, '');

  }


}


查看完整回答
反对 回复 2023-05-19
  • 2 回答
  • 0 关注
  • 153 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信