2 回答

TA貢獻(xiàn)1854條經(jīng)驗(yàn) 獲得超8個(gè)贊
TinyMCE 的noneditable
插件旨在使內(nèi)容塊不可編輯,但不可刪除。相反,它將不可編輯內(nèi)容的整個(gè)部分視為單個(gè)字符。
要阻止內(nèi)容被鍵盤(pán)刪除,您可以使用 Tiny 的事件處理結(jié)構(gòu)來(lái)查找某些按鍵,然后中斷/停止它們。
您需要展開(kāi)它以查看光標(biāo)在內(nèi)容中的位置,如果按鍵的結(jié)果會(huì)刪除您想要保留的內(nèi)容,則僅在這些情況下停止按鍵。
請(qǐng)注意,此方法不會(huì)阻止通過(guò)其他方法刪除內(nèi)容,例如將其作為更大選擇的一部分刪除。

TA貢獻(xiàn)1752條經(jīng)驗(yàn) 獲得超4個(gè)贊
編寫(xiě)了一個(gè)到目前為止運(yùn)行良好的 Angular 服務(wù),可能需要針對(duì)邊緣情況進(jìn)行一些調(diào)整。nonDeletableSelectors
包含表示應(yīng)該不可刪除的元素的 CSS 選擇器。我注意到顯然有一個(gè)帶有不可編輯元素的 TinyMCE 錯(cuò)誤,所以代碼比我想象的更復(fù)雜。
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, '');
? }
}
添加回答
舉報(bào)