如何在CKEDITOR textarea中阻止对内容的某些部分进行编辑?

时间:2012-07-04 12:01:25

标签: javascript plugins configuration ckeditor backspace

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

有没有办法阻止ckeditor textarea里面这个隐藏表的编辑?因此,当用户按退格键时,隐藏的表不受影响并保持不变。

只要CKEDITOR实例准备就绪,此源(下方)将被放入CkEditor Textarea(使用setData()属性),而User只能看到返回的<p></p>值。在这种情况下,它是<p>I really think I can do this!</p>。它描述了他的个人资料,他可以保留并编辑它。其余部分是隐藏的,只有在提交表单时才能在电子邮件中显示。奇怪的是,<p></p>位于顶部,但如果用户按下Backspace几次,表格将被删除,因此未提交。

    <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:test < / p>

我的配置文件:

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结构。

3 个答案:

答案 0 :(得分:4)

为了让它发挥作用,我不得不玩一下它。我在插件代码中添加了大量文档,如果您在阅读后有任何问题请通知我。

我要包含内容块的更新版本和插件代码块。


这是您更新的内容块。它包含在<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”的文件,并将下面的代码粘贴到该文件中。 我的错误:文件名为plugin.js,而不是插件.js。

如果您已经有额外的插件,请在extraPlugins配置设置中添加“cwjdsjcsconfineselection”,否则将此设置添加到您的配置中:
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");

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

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

就我而言,我希望通过在内容中添加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元素内编辑。所以我听'click'和'keyup'事件,如果它们在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();
        }
    });
});
相关问题