2 回答

TA貢獻1854條經驗 獲得超8個贊
TinyMCE 的noneditable
插件旨在使內容塊不可編輯,但不可刪除。相反,它將不可編輯內容的整個部分視為單個字符。
要阻止內容被鍵盤刪除,您可以使用 Tiny 的事件處理結構來查找某些按鍵,然后中斷/停止它們。
您需要展開它以查看光標在內容中的位置,如果按鍵的結果會刪除您想要保留的內容,則僅在這些情況下停止按鍵。
請注意,此方法不會阻止通過其他方法刪除內容,例如將其作為更大選擇的一部分刪除。

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, '');
? }
}
添加回答
舉報