How to make WordPress and TinyMCE accept tags wrapping block-level elements as allowed in HTML5?

[*]

Starting with version 5 the HTML standard allows <a> tags wrap block-level elements. On a specific page I need to wrap a heading and an image with an <a> tag:

Some intro text.

<div>
  <a href="http://somewhere/">
    <h4>Some heading</h4>
    <img src="https://somewhere/some-img.jpg" alt="Some image" />
  </a>
</div>

While I can enter this in the text editor it causes some strange behavior:

The code above will be transformed into this HTML code:

<p>Some intro text.</p>
<div>
<a href="http://somewhere/"></p>
<h4>Some heading</h4>
<p><img src="https://somewhere/some-img.jpg" alt="Some image" /><br />
</a>
</div>

Obviously, the opening <a> followed by a closing </p> for a never opened <p> is plain wrong. Also there’s some non-closed <p> tag before the <img> tag.


Since this seems to be a newline-related issue, I tried to remove newlines from my WordPress code:

Some intro text.

<div><a href="http://somewhere/"><h4>Some heading</h4><img src="https://somewhere/some-img.jpg" alt="Some image" /></a></div>

Interestingly, this results in the following HTML code:

<p>Some intro text.</p>
<div><a href="http://somewhere/"><br />
<h4>Some heading</h4>
<p><img src="https://somewhere/some-img.jpg" alt="Some image" /></a></div>

Now, there’s still a closing </p> tag missing after the <img>. (Okay, HTML5 accepts non-closed <p> tags… but I don’t think that this behavior is used intentionally here.) Also, WordPress introduces a <br /> that comes out of nowhere.

So far to the WordPress-related issues…


Now to the TinyMCE-related issues:

When switching back from the text edit mode in WordPress to the visual edit mode, the <a>s are still there. However, when switching back to text mode again (or saving the page from visual edit mode) the <a>s get completely removed.


Having this explained, let’s come to my main question: How can I make WordPress and TinyMCE accept <a> tags wrapping block-level elements?


Here’s what I’ve already tried:

  • Adding a filter to tiny_mce_before_init that sets TinyMCE’s valid_children setting for <a>s to include <h4>s (as suggested in the question “HTML5, WordPress and Tiny MCE issue – wrapping anchor tag around div results in funky output”)
  • Adding a filter to tiny_mce_before_init that sets TinyMCE’s schema setting to html5.

I also found the ticket “Block <a> tags are getting stripped from the Editor”, but don’t really understand if stripping <a> tags is considered intentional behavior there.

2 Answers
2

[*]

A few years down the track, but hopefully someone else will find this solution useful…

Although my issues trying to get this sort of thing to work in WordPress were slightly different, hopefully this solution will work for you too.

Firstly, the issue with tags not being allowed to contain multiple block level elements was solved by adding the following code from this article to my functions.php file…

function allow_all_tinymce_elements_attributes( $init ) {

    // Allow all elements and all attributes
    $ext="*[*]";

    // Add to extended_valid_elements if it already exists
    if ( isset( $init['extended_valid_elements'] ) ) {
        $init['extended_valid_elements'] .= ',' . $ext;
    } else {
        $init['extended_valid_elements'] = $ext;
    }

    // return value
    return $init;
}

add_filter('tiny_mce_before_init', 'allow_all_tinymce_elements_attributes');

… and then making sure that my tag is given a name. This allowed me to enter and successfully save the following in the Text tab…

Some text.
<div>
<a href="https://somewhere.com/product/this_prod/" name="link">
<h2>THIS PRODUCT</h2>
Cost efficient Blablabla</a>
</div>

For the second issue, of code being stripped when switching from the “text” to the “visual” tab, I simply turned off the visual tab for a particular post type. I adapted the initial code found in this article, which originally targeted a post ID instead of a post type. I added this code to my functions.php file…

add_filter('user_can_richedit', 'disable_wyswyg_to_preserve_my_markup');
function disable_wyswyg_to_preserve_my_markup( $default ){
  if( get_post_type() == 'product') return false;
  return $default;
}

If this doesn’t work for a custom post type, you could try adding the following line above the previous code…

add_theme_support( 'post-formats', 'my_custom_post_type' );

Or, to solve this second issue, you could alternatively try a plugin such as this one.

Leave a Comment