Joel Peltonen
Joel Peltonen

Reputation: 13402

Prevent attributes from being copied when entering new paragraph?

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

Answers (3)

colin
colin

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

Panda
Panda

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

Reinmar
Reinmar

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

Related Questions