Jump to content
Robin S

Using the match callback for allowedContent/disallowedContent in CKEditor

Recommended Posts

A pet hate of mine is when an editor uses a paragraph of bold text for what ought to be a heading. When I need to tidy up poorly formatted content like this I will quickly change such lines of text into the heading of the appropriate level, but that still results in markup like...

<h2><strong>Some heading text</strong></h2>

The <strong> has no business being there, but it's a bit of a hassle to remove it because you have to drag a selection around the exact text as opposed to just placing your cursor within the line. That gets tedious if you have a lot content to process.

I figured there has to be an easier way so started looking into the ACF (Advanced Content Filter) features of CKEditor. What I wanted is a rule that says "strong tags are disallowed specifically when they are within a heading tag". (I guess there could occasionally be a use case where it would be reasonable to have a strong tag within a heading tag, but it's so rare that I'm not bothered about it).

With the typical string format for allowedContent and disallowedContent there is no ability to disallow a specific tag only when it is within another specific tag - a tag is allowed everywhere or not at all. But I found there is an alternative object format for these rules that supports a callback function in the "match" property. So I was able to achieve my goal with the following in /site/modules/InputfieldCKEditor/config.js:

CKEDITOR.editorConfig = function(config) {
    config.disallowedContent = {
        // Rule for the <strong> element
        strong: {
            // Use "match" callback to determine if the element should be disallowed or not
            match: function(element) {
                // Heading tag names
                var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
                // The parent of the element (if any)
                var parent = element.parent;
                if(typeof parent !== 'undefined') {
                    // If there is a parent, return true if its name is in the heading names array
                    return headings.indexOf(parent.name.toLowerCase()) !== -1;
                } else {
                    // There is no parent so the element is allowed
                    return false;
                }
            }
        }
    }
};

 

Another tip: if you want to debug your allowedContent or disallowedContent rules to make sure they are being parsed and applied successfully you can log the filter rules to the console. For convenience I used /site/modules/InputfieldCKEditor/config.js again.

// Get the CKEditor instance you want to debug
var editor = CKEDITOR.instances.Inputfield_body;
editor.on('instanceReady', function() {
    // Log the disallowed content rules
    console.log(editor.filter.disallowedContent);
});

 

  • Like 6

Share this post


Link to post
Share on other sites

Update...

The above works okay but it seems that the match callback only fires when CKEditor loads, so to be sure that any disallowed content resulting from the current page edit is removed you have to save the page twice.

After a bit more hunting I think I've found a better approach that uses CKEditor's DTD object. It's not quite as straightforward as you'd expect at first because element objects within the DTD object are not fully independent (e.g. CKEDITOR.dtd['h2'] seems to refer to the same object as CKEDITOR.dtd['p']). This SO post helped me find a solution.

In /site/modules/InputfieldCKEditor/config.js:

// For numbers 1 to 6
for(var i = 1; i <= 6; i++) {
    // Create the tag name from 'h' plus the number
    var tag = 'h' + i;
    // Create clone of DTD heading object so it can be modified individually
    CKEDITOR.dtd[tag] = Object.assign({}, CKEDITOR['dtd'][tag]);
    // Disallow strong element from being contained within heading element
    CKEDITOR.dtd[tag]['strong'] = 0;
}

 

  • Like 5

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By prestoav
      Hi all and thanks for the great work on PW!

      One thing I find I have to do on any new site is add the Justify plugin to textarea fields as it is such a widely required feature for text headings in content.

      ANy chance this could be added to the core an automatically be installed on new textarea fields using CKEditor?

      Thank you!
    • By AndréPortuga
      Does anyone knows if there is a way of selecting a iframe inside a textfield(using ckEditor)? 
      I mean I have a Iframe with this code:
      <iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" scrolling="no" src="https://www.youtube.com/embed/PMaFJjZDrYM" width="100%"></iframe></p>
       
      but it's not getting responsive in iPhones.. So anyone know how can I select it via code for making it responsive?
       
      Thank you,
    • By dragan
      On one particular site, CKEditor behaves rather weird:
      When I copy and paste "normally", i.e. CTRL + V, there is an alert popping up "do you really want to leave this page?". If you cancel, the text is pasted into the field. If not, you get redirected to the frontend of the page you just edited, and the changes are lost.
      The "paste from Word" button doesn't work. CTRL + SHIFT + V works, but all formatting is lost (apart from paragraphs).
      I'm using inline editor mode, ACF + Purifier are activated. I know that if I disable those two, CTRL + V works, but I won't do that, since CKE then saves all the garbage code from MS-Office.
      Has anyone ever come across this issue? I tried with Chrome + FF. No JS errors.
    • By AndZyk
      Hello,
      I am currently building a intranet which will be hosted on the local network of a company. This intranet has many links to files on their fileserver with the protocol file://.
      So for example the links look like this file://domain.tld/filename.ext
      When I try to insert such a link into a URL field, I get the error, that only the protocol http:// is allowed. When I try to insert such a link into a CKEeditor link, it gets stripped out. Is it possible to insert such links into the FieldType URL and CKEditor.
      I know that I could use a FieldType Text or insert a RewriteRule in the .htaccess file, but I am looking for a more elegant solution. 😉
      Regards, Andreas
    • By prestoav
      Hi folks.
      I'm building a Formbuilder form to create new advert pages in a marketplace. Formbuilder is ideal for this. I need to add a description field that allows text to be added with line breaks etc. so showing a CKEditor field would be ideal (i.e. exactly like the field I'm typing into right now)!.
      Does anyone know if it's possible to add a CKEditor field to the front end presentation fo a Formbuilder field, maybe via a module or such?
      Many thanks!
×
×
  • Create New...