Reputation: 13402
When creating new paragraphs in CKEditor the attributes (styles, classes) of the previous paragraph get copied onto the new one. Is there a way to prevent this?
For example, if I'm writing in a centered paragraph and press enter to create a new paragraph, my users want the new paragraph to be a simple
without "inheriting" anything from the previous by default.
Edit
I managed to get it (dangeriously untested) working with Reinmar's tips. Here is what I wound up with; I hope this helps someone else. If you guys see a glaring error here, please do tell me
CKEDITOR.on('instanceCreated', function(e) {
e.editor.on('key', function(evt) {
if (evt.data.keyCode === 13) {
// if we call getStartElement too soon, we get the wrong element
setTimeout(function () {
var se = e.editor.getSelection().getStartElement();
if(se.getName() == "span") {
var text = se.getText(); // Store text, we are about to nuke the spans
while (se.getName() == "span") { // possible infinite loop danger
se = se.getParent();
}
if (text.length == 0)
se.setHtml(" "); // It's important that this is not empty
else
se.setHtml(text);
}
debug(se.getHtml());
se.removeAttribute("class");
se.removeAttribute("mycustomattr");
se.removeAttribute("myothercustomattr");
window.bla = se; // useful for debugging
}, 10);
}
});
});
Upvotes: 10
Views: 1964
Reputation: 153
This is an old post, but I've been having this same issue and I finally got something working so I wanted to share my solution. For those who don't know why we would want this functionality, here's my explanation:
By default, CKEDITOR attempts to allow the user to continue with the same styling when pressing the enter key to insert new lines. In other words, if your text is bold and centered and you press the enter key, ckeditor replicates that styling so that the new line is also bold and centered. This is great until you want to insert something more complicated, like nested elements with attributes (such as via a plugin). When you hit enter within an element that has a class, ckeditor creates a new paragraph element with the same class which won't work if that class relies on the presence of a parent element (Here's an example where this becomes an issue: http://jsfiddle.net/B4yGJ/158/).
The solution I came up with combines code and suggestions from Reinmar and Nenotlep:
// create object of safe classes
var safe_classes = {};
$.each(ckeditor_styles_full, function(i, style) {
if(style['attributes']) {
var style_classes = style['attributes'].class;
var style_element = style['element'];
if(typeof safe_classes[style_element] != "object") {
safe_classes[style_element] = [];
}
style_classes = style_classes.split(" ");
$.each(style_classes, function(i, v) {
if($.inArray(v, safe_classes[style_element]) <= -1) {
safe_classes[style_element].push(v);
}
});
}
});
$.each(CKEDITOR.instances, function(i, editor) {
editor.on('key', function(e) {
if (e.data.keyCode === 13) {
setTimeout(function () {
var se = e.editor.getSelection().getStartElement();
var parent = se.getParent().getName();
if(parent == "html" || parent == "body") {
var se_classes = se.getAttribute('class');
var new_classes = [];
se.removeAttribute("class");
if(se_classes !== null) {
se_classes = se_classes.split(" ");
$.each(se_classes, function(i, v) {
if(typeof safe_classes[se.getName()] != 'undefined') {
if($.inArray(v, safe_classes[se.getName()]) > -1) {
se.addClass(v);
}
}
});
}
}
}, 10);
}
});
});
Here's how it works: First, the script scrapes the selected styles list for class names and compiles them into an object by element type. Then, it iterates over all instances of CKEDITOR binding an event to the enter key. Next, it checks to see if the element created when you pressed enter is inside a parent element other than HTML or BODY (i.e., it's part of a larger element tree). Finally, if it isn't inside a container element, it strips all of the classes from that element that aren't in the "safe_classes" object created earlier.
2 important side notes: 1) You still need to set "forceEnterMode: true" 2) Because "forceEnterMode" is set to true, it's best to only nest paragraph elements. Example:
<div class="container"><p class="title">Title Text</p><p class="content">Content Goes Here</p></div>
Otherwise, if you press enter inside a nested div, ckeditor will duplicate all of that div's attributes into a paragraph element.
Upvotes: 2
Reputation: 121
I had the similar issue with the upcoming CKEditor v4 (current nightlies). I insert a paragraph with image and custom classes from my custom image browser and CKEditor kept copying the classes to paragraphs created after the image.
I didn't like the idea of key
event followed by call of setTimeout
, so I examined the source a little bit and it turned out to be really simple.
When you press enter, CKEditor actually issues the enter
command. All you need to do, is set an afterCommandExec
event and find the newly created empty element:
editor.on('afterCommandExec', function (e) {
if (e.data.name == 'enter') {
var el = e.editor.getSelection().getStartElement();
// when splitting a paragrah before it's first character,
// the second one is selected
if (el.getHtml() != '<br>') {
if (el.hasPrevious() && el.getPrevious().getHtml() == '<br>') {
el = el.getPrevious();
} else {
// both paragraphs are non-empty, do nothing
return;
}
}
// modify el according to your needs
}
});
Hope this helps!
Upvotes: 12
Reputation: 22023
I was thinking about your question for last two days and I've got an idea. I checked enter plugin code and it'd better to leave it untouched. Instead you can listen on enter key and after our custom enter is performed you should clear styles from newly created block.
These methods will be useful:
editor.on( 'key', function( evt ) { evt.data.key... } )
editor.getSelection().getStartElement()
- after enter selection start will be placed in newly created block (+ inline elements like bold, underline, etc).CKEDITOR.dtd.*
- sets of elements may help you making decision which elements are inline styles and should be removed.element.isEmptyInlineRemoveable
- you should remove empty inline elements in which cursor was placed.editor.createRange().setStartAt( block, 0 ).select()
- at the end you should place caret in correct place (at the beginning of block - <p>
/<div>
/<li>
/etc.).Unfortunately, as you can see, this's not an easy thing to write, so good luck :)
Upvotes: 1