如何阻止编辑 CKEDITOR 文本区域中内容的某些部分

How to block editing on certain part of content in CKEDITOR textarea?

本文关键字:些部 编辑 何阻止 CKEDITOR 文本 区域      更新时间:2023-09-26

>我的 CKEDITOR 表单预填充了隐藏表,该表与用户输入的文本一起提交。这工作正常,但有时用户按退格键的次数过多并删除隐藏的表。

有没有办法阻止在 ckeditor 文本区域中编辑这个隐藏表?因此,当用户按退格键时,隐藏表不受影响并保留在内。

一旦 CKEDITOR 实例准备就绪,此源代码(波纹管)就会放入 CkEditor Textarea 中(使用 setData() 属性),用户只能看到返回的<p></p>值。在这种情况下,它的<p>I really think I can do this!</p>.这是对他的个人资料的描述,他可以保留和编辑它。其余部分是隐藏的,仅在提交表单时在电子邮件中可见。奇怪的是<p></p>在顶部,但是如果用户按几次退格键,表格将被删除,因此不会提交。

    <span id="messageTemplate1" class="message">
<p>I really think I can do this!</p>
<table class="hide" style="font-size: 12px;">
    <tbody>
        <tr class="hide">
            <td>
            Application sent by <strong><a href="http://www.globalcastingcenter.com/talent/jack-bolton">Matt Faro</a></strong> for Audition: <a href="http://www.globalcastingcenter.com:80/CustomContentRetrieve.aspx?ID=4185493">Actors Needed</a>
            </td>
        </tr>
        <tr class="hide">
            <td>
            Reply to applicant directly: mantas@mantas.co or visit full profile: http://www.globalcastingcenter.com/talent/jack-bolton
            </td>
        </tr>
    </tbody>
</table>
<table class="hide" style="font-size: 12px;">
    <tbody>
        <tr class="hide">
            <td><strong>Short Profile Summary:</strong></td>
        </tr>
    </tbody>
</table>
<table class="hide" style="font-size: 12px;">
    <tbody>
        <tr class="hide">
            <td>
            <a href="http://www.globalcastingcenter.com/talent/jack-bolton"><img alt="" src="http://globalcastingcenter.com/talent_images/4164035_258551_foto.png?Action=thumbnail&amp;Width=144&amp;Height=215" /></a>
            </td>
        </tr>
    </tbody>
</table>
<table style="font-size: 12px;" class="hide">
    <tbody>
        <tr class="hide">
            <td><strong>Areas:</strong></td>
            <td>Actor,Extra</td>
        </tr>
        <tr class="hide">
            <td><strong>Country:</strong></td>
            <td>WORLDWIDE,Any</td>
        </tr>
        <tr class="hide">
            <td><strong>Age:</strong></td>
            <td>26</td>
        </tr>
    </tbody>
</table>
</span>

现在,当我加载您的插件时,我的CKeditor框消失了,请在测试页面上按"应用" http://gcc-july.themantas.co.uk/auditions/actors-needed 请先登录以便能够访问消息框 登录名: tiknius@gmail.com pssw: 测试

我的配置文件:

CKEDITOR.editorConfig = function( config )
{
    config.toolbar = 'MyToolbar';
    config.toolbar_MyToolbar =
    [
        { name: 'clipboard', items : [ 'Undo','Redo' ] },           
        { name: 'styles', items : ['FontSize' ] },
        { name: 'basicstyles', items : [ 'Bold','Italic'] },
        { name: 'paragraph', items : ['Outdent','Indent' ] },
    ];
    config.removePlugins = 'contextmenu';
    config.forcePasteAsPlainText = true;   
    config.pasteFromWordRemoveFontStyles = true;
    config.pasteFromWordRemoveStyles = true;
    config.extraPlugins = 'cwjdsjcsconfineselection';
    config.startupShowBorders = false;
    config.disableObjectResizing = true;
};

这是我禁用您的插件时框的外观:http://screencast.com/t/Kc2bIOU8md2

我使用您建议的HTML结构。

我不得不稍微玩一下才能让它工作。我在插件代码中添加了很多文档,如果您在阅读后有任何疑问,请告诉我。

我正在包括您的内容块和插件代码块的更新版本。


这是您更新的内容块。当包装在 <span> 标签中时它不起作用,所以我把它包装在一个表格中。

您可能不喜欢数据单元格周围显示的边框和调整大小轮廓,如果是这种情况,请将这些设置添加到您的配置中:
config.startupShowBorders = false; config.disableObjectResizing = true;

一些注意事项:
在需要起始内容之前的空<td>,它会阻止用户使用"Ctrl A"选择允许他们删除隐藏表的所有内容><。

我从起始内容中删除了 <p> 标签,因为它在这个结构中表现得很时髦.

保存隐藏表的<td>具有&nbsp;字符,它可以防止用户使用"Ctrl A"选择允许他们删除隐藏表的所有内容。如果您删除光标右侧的所有内容,则会导致光标丢失,但您可以单击内容再次开始编辑.

contenteditable="false"属性由 CkEditor 使用并且是必需的,但它不能完成整个工作。您可以在不激活插件的情况下试用新的 HTML,看看它本身有什么效果.

插件代码中有关于我使用的类和 ID 的注释。

<!-- Begin Wrapper Table that Replaces <span> element -->
<table id="messageTemplate1" class="message cwjdsjcs_editable">
    <tbody>
        <tr>
            <td class="cwjdsjcs_not_editable" contenteditable="false">
            </td>
            <td id="cwjdsjcs_editable_id">
                I really think I can do this!
            </td>
        </tr>
        <tr class="cwjdsjcs_not_editable" contenteditable="false">
            <td colspan="2">
                &nbsp;
                <!-- Begin Original Content -->
                <table class="hide" style="font-size: 12px; display:none;">
                    <tbody>
                        <tr class="hide">
                            <td>
                            Application sent by <strong><a href="http://www.globalcastingcenter.com/talent/jack-bolton">Matt Faro</a></strong> for Audition: <a href="http://www.globalcastingcenter.com:80/CustomContentRetrieve.aspx?ID=4185493">Actors Needed</a>
                            </td>
                        </tr>
                        <tr class="hide">
                            <td>
                            Reply to applicant directly: mantas@mantas.co or visit full profile: http://www.globalcastingcenter.com/talent/jack-bolton
                            </td>
                        </tr>
                    </tbody>
                </table>
                <table class="hide" style="font-size: 12px; display:none;">
                    <tbody>
                        <tr class="hide">
                            <td><strong>Short Profile Summary:</strong></td>
                        </tr>
                    </tbody>
                </table>
                <table class="hide" style="font-size: 12px; display:none;">
                    <tbody>
                        <tr class="hide">
                            <td>
                            <a href="http://www.globalcastingcenter.com/talent/jack-bolton"><img alt="" src="http://globalcastingcenter.com/talent_images/4164035_258551_foto.png?Action=thumbnail&amp;Width=144&amp;Height=215" /></a>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <table style="font-size: 12px; display:none;" class="hide">
                    <tbody>
                        <tr class="hide">
                            <td><strong>Areas:</strong></td>
                            <td>Actor,Extra</td>
                        </tr>
                        <tr class="hide">
                            <td><strong>Country:</strong></td>
                            <td>WORLDWIDE,Any</td>
                        </tr>
                        <tr class="hide">
                            <td><strong>Age:</strong></td>
                            <td>26</td>
                        </tr>
                    </tbody>
                </table>
                <!-- End Original Content -->
            </td>
        </tr>
    </tbody>
</table>
<!-- End Wrapper Table that Replaces <span> element -->

这是插件代码,它被称为"cwjdsjcsconfineselection"。

要添加插件:
在插件目录中创建一个名为"cwjdsjcsconfineselection"的文件夹:ckeditor/plugins/
在该目录中创建一个名为"plugins.js"的文件,并将以下代码粘贴到该文件中。我的错误:文件被命名为插件.js,而不是插件.js。

如果您已经有额外的插件,请将"cwjdsjcsconfineselection"添加到extraPlugins配置设置中,否则将此设置添加到您的配置中:
config.extraPlugins = 'cwjdsjcsconfineselection';

该插件应该在下次加载编辑器时工作。

对于我的情况,当用户单击不可编辑区域时,我会出现一个对话框,以解释为什么光标被移回上一个选择。这对您的使用似乎不是必需的,所以我将其注释掉了。

/*
  Plugin that prevents editing of elements with the "non-editable" class as well as elements outside of blocks with "editable" class.
*/
//* **************************  NOTES  ***************************  NOTES  ****************************
/*
  The "lastSelectedElement" variable is used to store the last element selected.
  This plugin uses the "elementspath" plugin which shows all elements in the DOM
  parent tree relative to the current selection in the editing area.
  When the selection changes, "elementsPathUpdate" is fired,
  we key on this and loop through the elements in the tree checking the classes assigned to each element.
  Three outcomes are possible.
  1) The non-editable class is found:
  Looping stops, the current action is cancelled and the cursor is moved to the previous selection.
  The "selectionChange" hook is fired to set the reverted selection throughout the instance.
  2) The editable class is found during looping, the "in_editable_area" flag is set to true.
  3) Neither the editable or the non-editable classes are found (user clicked outside your main container).
  The "in_editable_area" flag remains set to false.
  If the "in_editable_area" flag is false, the current action is cancelled and the cursor is moved to the previous location.
  The "selectionChange" hook is fired to set the reverted selection throughout the instance.
  If the "in_editable_area" flag is true,
  the "lastSelectedElement" is updated to the currently selected element and the plugin returns true.
---------------
  If you don't want the elements path to be displayed at the bottom of the editor window,
  you can hide it with CSS rather than disabling the "elementspath" plugin.
  The elementspath plugin creates and is left active because we are keying on changes to the path in our plugin.
  #cke_path_content
  {
    visibility: hidden !important;
  }
---------------
  CSS Classes and ID that the plugin keys on. Use defaults or update variables to use your preferred classes and ID:
  var starting_element_id = ID of known editable element that always occurs in the instance.
  Don't use elements like <table>, <tr>, <br /> that don't contain HTML text.
  Default value = cwjdsjcs_editable_id
  var editable_class = class of editable containers.
  Should be applied to all top level elements that contain editable elements.
  Default = cwjdsjcs_editable
  var non_editable_class = class of non-editable elements within editable containers
  Apply to elements where all child elements are non-editable.
  Default = cwjdsjcs_not_editable
*/
//* **************************  END NOTES  ***************************  END NOTES  ****************************

// Register the plugin with the editor.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.plugins.html
CKEDITOR.plugins.add( 'cwjdsjcsconfineselection',
{
  requires : [ 'elementspath' ],
  // The plugin initialization logic goes inside this method.
  // http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.pluginDefinition.html#init
  init: function( editor )
  {
    editor.on( 'instanceReady', function( instance_ready_data )
    {
      // Create variable that will hold the last allowed selection (for use when a non-editable selection is made)
      var lastSelectedElement;
      editor.cwjdsjcs_just_updated = false;
      // This section starts things off right by selecting a known editable element.
      // *** Enter the ID of the element that should have initial focus *** IMPORTANT *** IMPORTANT ***
      var starting_element_id = "cwjdsjcs_editable_id";
      var resInitialRange = new CKEDITOR.dom.range( editor.document );
      resInitialRange.selectNodeContents( editor.document.getById( starting_element_id ) );
      resInitialRange.collapse();
      var selectionObject = new CKEDITOR.dom.selection( editor.document );
      editor.document.focus();
      selectionObject.selectRanges( [ resInitialRange ] );
      var sel = editor.getSelection();
      var firstElement = sel.getStartElement();
      var currentPath = new CKEDITOR.dom.elementPath( firstElement );
      // Set path for known editable element, fire "selectionChange" hook to update selection throughout instance.
      editor._.selectionPreviousPath = currentPath;
      editor.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } );
    }); // *** END - editor.on( 'instanceReady', function( e )

    // When a new element is selected by the user, check if it's ok for them to edit it,
    // if not move cursor back to last know editable selection
    editor.on( 'elementsPathUpdate', function( resPath )
    {
      // When we fire the "selectionChange" hook at the end of this code block, the "elementsPathUpdate" hook fires.
      // No need to check because we just updated the selection, so bypass processing.
      if( editor.cwjdsjcs_just_updated == true )
      {
        editor.cwjdsjcs_just_updated = false;
        return true;
      }
      var elementsList = editor._.elementsPath.list;
      var in_editable_area = false;
      var non_editable_class = "cwjdsjcs_not_editable";
      var editable_class = "cwjdsjcs_editable";
      for(var w=0;w<elementsList.length;w++){
        var currentElement = elementsList[w];
        // Sometimes a non content element is selected, catch them and return selection to editable area.
        if(w == 0)
        {
          // Could change to switch.
          if( currentElement.getName() == "tbody" )
          {
            in_editable_area = false;
            break;
          }
          if( currentElement.getName() == "tr" )
          {
            in_editable_area = false;
            break;
          }
        }
        // If selection is inside a non-editable element, break from loop and reset selection.
        if( currentElement.hasClass(non_editable_class) )
        {
          in_editable_area = false;
          break;
        }
        if( currentElement.hasClass(editable_class) ) {
          in_editable_area = true;
        }
        console.log(currentElement);
        console.log(currentElement.getName());
      }
      // if selection is within an editable element, exit the plugin, otherwise reset selection.
      if( in_editable_area ) {
        lastSelectedElement = elementsList[0];
        return true;
      }
      var resRange = new CKEDITOR.dom.range( editor.document );
      resRange.selectNodeContents( lastSelectedElement );
      resRange.collapse();
      editor.getSelection().selectRanges( [ resRange ] );
      resRange.endContainer.$.scrollIntoView();
      // Open dialog window:
      // It tells user they selected a non-editable area and cursor has been returned to previous selection
//      currentEditorName = editor.name;
//      openResDefaultDialog(currentEditorName);
      try
      {
        var sel = editor.getSelection();
        var firstElement = sel.getStartElement();
        var currentPath = new CKEDITOR.dom.elementPath( firstElement );
        editor.cwjdsjcs_just_updated = true;
        editor._.selectionPreviousPath = currentPath;
        editor.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } );
      }
      catch (e)
      {}
    });
  } // *** END - init: function( editor )
}); // ************************************************************************************* END - CKEDITOR.plugins.add

要测试插件是否已加载,请在实例就绪触发器后添加警报:

    editor.on( 'instanceReady', function( instance_ready_data )
    {
      alert("instanceReady");

要测试在选择更改时是否触发插件,请在 elementsPathUpdate 触发器后添加警报:

    editor.on( 'elementsPathUpdate', function( resPath )
    {
      alert("elementsPathUpdate");

我意识到这已经关闭并解决了,但这里有一个选项:

在用户提交表单或以您的 CKE 内容使用方式后及时添加表。只是不要添加不可见的表格,但是当用户单击"提交"时,将其添加到正在发布的任何内容中。或者,如果您以后需要编辑它,只需在插入编辑器之前将其删除,然后在发布之前再次及时添加它。没有黑客攻击 CKE 核心,也不需要插件,因为操作发生在 CKE 之外。

我有类似的要求,但在网上找不到答案。

就我而言,我想通过在内容中添加 pre 元素来支持编辑器中的制表符:

editor.on('key', function(ev) {
    if (ev.data.keyCode == 9) { // TAB
        // data-tab attribute added so we can identify it later
        var tabHtml = '<pre data-tab="true" style="display:inline;">&#09;</pre>';
        var tabElement = CKEDITOR.dom.element.createFromHtml(tabHtml, editor.document);
        editor.insertElement(tabElement);
        ev.cancel();
    }
});

但现在用户可以使用箭头键或单击鼠标并开始在pre元素内进行编辑。所以我听"单击"和"键控"事件,如果它们在pre内,则移动光标。

// this prevents the user from typing inside the tab span
var moveCursorOutOfTab = function() {
    var ranges = editor.getSelection().getRanges();
    if (ranges.length == 1) {
        var parent = ranges[0].startContainer.getParent();
        // is the cursor is inside a tab element
        if (parent && parent.getAttribute('data-tab') == 'true') {
            var newRange = editor.createRange();
            newRange.setStartAfter(parent);
            newRange.setEndAfter(parent);
            // move the cursor after the tab element
            editor.getSelection().selectRanges([newRange]);
        }
    }
};
// wait until the editor is initialized
editor.on('contentDom', function() {
    var editable = editor.editable();
    // listen for clicks
    editable.attachListener(editable, 'click', function(ev) {
        moveCursorOutOfTab();
    });
    // must use the keyup event because we want to
    // act after any cursor movements
    editor.document.on('keyup', function(ev) {
        // check for left and right arrow keys
        if (ev.data.getKey() == 37 || ev.data.getKey() == 39) {
            moveCursorOutOfTab();
        }
    });
});