javascript 如何阻止编辑 CKEDITOR textarea 中的某些部分内容?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11328681/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to block editing on certain part of content in CKEDITOR textarea?
提问by Mantas
I have my CKEDITOR form prepopulated with hidden table which is being submitted together with user inputed text. This works fine, but sometimes user presses backspace too many times and deletes the hidden table.
我的 CKEDITOR 表单预填充了隐藏表,该表与用户输入的文本一起提交。这工作正常,但有时用户多次按下退格键并删除隐藏表。
Is there a way to block editing on this hidden table inside ckeditor textarea? So when user presses backspace the hidden table isn't affected and stays in.
有没有办法阻止在 ckeditor textarea 中的这个隐藏表上进行编辑?因此,当用户按下退格键时,隐藏的表格不会受到影响并保持不变。
As soon as CKEDITOR instance is ready this source (bellow) is put inside CkEditor Textarea (using setData() attribute) and User only sees the returned <p></p>
value. In this case its <p>I really think I can do this!</p>
. Its a description of his profile and he can keep it and edit it. The rest is hidden and only visible in e-mail when form is submitted. Its strange that <p></p>
is on top but if user presses Backspace couple times the table gets deleted and therefor not submitted.
一旦 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/Hyman-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: [email protected] or visit full profile: http://www.globalcastingcenter.com/talent/Hyman-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/Hyman-bolton"><img alt="" src="http://globalcastingcenter.com/talent_images/4164035_258551_foto.png?Action=thumbnail&Width=144&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>
Now when I load your plugin my CKeditor box disapears, please press "Apply" on testing page http://gcc-july.themantas.co.uk/auditions/actors-neededplease login first to be able to access the message box Login name: [email protected] pssw: test
现在,当我加载您的插件时,我的 CKeditor 框消失了,请在测试页面上按“应用” http://gcc-july.themantas.co.uk/auditions/actors-needed请先登录才能访问消息框 登录姓名:[email protected] pssw:测试
My config file:
我的配置文件:
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;
};
This is how the box looks when I disable your plugin: http://screencast.com/t/Kc2bIOU8md2
这是我禁用您的插件时框的外观:http: //screencast.com/t/Kc2bIOU8md2
I use your suggested HTML structure.
我使用您建议的 HTML 结构。
采纳答案by codewaggle
I had to play around with it a bit to get it to work. I added lots of documentation to the plugin code, if you have any questions after reading it through let me know.
我不得不玩弄它才能让它工作。我在插件代码中添加了大量文档,如果您在阅读后有任何疑问,请告诉我。
I'm including an updated version of your content block and the plugin code block.
我包括您的内容块和插件代码块的更新版本。
Here is your updated content block. It wasn't working when wrapped in the <span>
tag, so I wrapped it in a table.
这是您更新的内容块。包裹在<span>
标签中时它不起作用,所以我将它包裹在表格中。
You may not like the border and resizing outlines that appear around the data cell, if that's the case, add these settings to your configuration:config.startupShowBorders = false;
config.disableObjectResizing = true;
您可能不喜欢出现在数据单元格周围的边框和调整大小的轮廓,如果是这种情况,请将这些设置添加到您的配置中:config.startupShowBorders = false;
config.disableObjectResizing = true;
Some notes:
The empty <td>
before your starting content is needed, it prevents the user from using "Ctrl A" to select everything which would allow them to delete the hidden table.
一些注意事项:需要开始内容之前
的空白<td>
,它可以防止用户使用“Ctrl A”来选择允许他们删除隐藏表格的所有内容。
I removed the <p>
tag from the starting content as it acted funky in this structure.
我<p>
从起始内容中删除了标签,因为它在这个结构中表现得很时髦。
The <td>
that holds the hidden tables has a
character, it prevents the user from using "Ctrl A" to select everything which would allow them to delete the hidden table. It causes the cursor to get lost if you delete everything to the right of the cursor, but you can click on the content to begin editing again.
将<td>
持有的隐藏的表有一个
特点,它可以防止用户使用“Ctrl键A”选择所有这将让他们删除表中隐藏。如果删除光标右侧的所有内容,会导致光标丢失,但您可以单击内容重新开始编辑。
The contenteditable="false"
attribute is used by CkEditor and is needed, but it doesn't do the whole job. You can try out the new HTML without activating the plugin to see what effect it has by itself.
该contenteditable="false"
属性由 CkEditor 使用并且是必需的,但它并不能完成全部工作。您可以在不激活插件的情况下试用新的 HTML,看看它本身有什么效果。
There are notes in the plugin code about the classes and ID I used.
插件代码中有关于我使用的类和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">
<!-- 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/Hyman-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: [email protected] or visit full profile: http://www.globalcastingcenter.com/talent/Hyman-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/Hyman-bolton"><img alt="" src="http://globalcastingcenter.com/talent_images/4164035_258551_foto.png?Action=thumbnail&Width=144&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 -->
Here's the plugin code, it's called "cwjdsjcsconfineselection".
这是插件代码,它被称为“cwjdsjcsconfineselection”。
To add the plugin:
Create a folder called "cwjdsjcsconfineselection" in the plugins directory: ckeditor/plugins/
Create a file called "plugins.js" in that directory and paste the code below into that file. My mistake: file is named plugin.js, not plugin(s).js.
添加插件:
在 plugins 目录中创建一个名为“cwjdsjcsconfineselection”的文件夹:在该目录中ckeditor/plugins/
创建一个名为“plugins.js”的文件并将下面的代码粘贴到该文件中。我的错误:文件名为 plugin.js,而不是 plugin(s).js。
If you already have extra plugins, add "cwjdsjcsconfineselection" to the extraPlugins config setting, otherwise add this setting to your configuration:config.extraPlugins = 'cwjdsjcsconfineselection';
如果您已经有额外的插件,请将“cwjdsjcsconfineselection”添加到 extraPlugins 配置设置中,否则将此设置添加到您的配置中:config.extraPlugins = 'cwjdsjcsconfineselection';
The plugin should work the next time you load the editor.
下次加载编辑器时,该插件应该可以工作。
For my situation, I have a dialog box appear when the user clicks in a non-editable area to explain why the cursor was moved back to the previous selection. That doesn't seem necessary for your usage, so I commented it out.
对于我的情况,当用户单击不可编辑区域时会出现一个对话框,以解释为什么将光标移回先前的选择。这对于您的使用来说似乎没有必要,所以我将其注释掉了。
/*
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
To test that the plugin is loaded add an alert after the instance ready trigger:
要测试插件是否已加载,请在实例就绪触发器后添加警报:
editor.on( 'instanceReady', function( instance_ready_data )
{
alert("instanceReady");
To test that the plugin is being triggered when the selection changes, add an alert after the elementsPathUpdate trigger:
要测试选择更改时是否触发插件,请在 elementsPathUpdate 触发器后添加警报:
editor.on( 'elementsPathUpdate', function( resPath )
{
alert("elementsPathUpdate");
回答by Nenotlep
I realize that this is closed and solved, but here's an option:
我意识到这已经关闭并解决了,但这里有一个选项:
Add the table just in time after the user submits your form or however your CKE content is being used. Simply don't add the invisible table, but when the user clicks "submit", add it then to whatever is being posted. Alternatively, if you need to edit it later, just remove it before inserting to the editor and then add it once again Just In Time before posting. No hacking of the CKE core and no plugins required as the action happens outside CKE.
在用户提交您的表单或使用您的 CKE 内容后及时添加表格。简单地不要添加不可见的表格,但是当用户单击“提交”时,将其添加到正在发布的任何内容中。或者,如果您稍后需要编辑它,只需在插入编辑器之前将其删除,然后在发布前再次及时添加。无需对 CKE 核心进行黑客攻击,也无需插件,因为该操作发生在 CKE 之外。
回答by lance-java
I had a similar requirement but didn't find my answer online.
我有类似的要求,但没有在网上找到我的答案。
In my case, I want to support the tab character in the editor by adding a pre
element to the content:
就我而言,我想通过向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;">	</pre>';
var tabElement = CKEDITOR.dom.element.createFromHtml(tabHtml, editor.document);
editor.insertElement(tabElement);
ev.cancel();
}
});
But now the user can use the arrow keys or click the mouse and start editing inside the pre
element. So I listen to the 'click' and 'keyup' events and move the cursor if they are inside the pre
.
但是现在用户可以使用箭头键或单击鼠标并在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();
}
});
});