WordPress Visual Editor Stripping HTML Changes

Background

This issue revolves around the removal of empty <span></span> elements in TinyMCE when saving a post or switching between between the Visual and Text editor tabs. The problem is inherent to core functionality (as of trunk version 4.0-alpha-20140602) and is unchangeable via the standard WordPress APIs, as is mentioned in ticket #26986. The ticket details the reasoning behind the setting and is marked as wont-fix, meaning that your best bet is one of the “work-arounds” listed below.

Default WordPress/TinyMCE Behavior

By default, WordPress’s TinyMCE instance is configured such that a number of empty tags or tags missing certain attributes are removed in an attempt to eliminate markup that does not do anything.

From MDN’s <span> docs:

The HTML element is a generic inline container for phrasing
content, which does not inherently represent anything. It can be used
to group elements for styling purposes (using the class or id
attributes), or because they share attribute values, such as lang. It
should be used only when no other semantic element is appropriate.

As <span> is intended to group inline elements/content, the WordPress core contributors have decided that the element must be irrelevant when the tag is empty and thus strip it from the content.


Work-Arounds

For the most part, the proper way to modify TinyMCE’s configuration is to hook into tiny_mce_before_init with a filter however span‘s empty-removal behavior cannot be modified in this manner. To do so likely requires hacks to core files, and as such I have not provided any such solutions for consideration.

Ideal: Give <span> Screen Reader Text

Screen readers are pieces of software that attempt to interpret on-screen content in order to aide the visually-impaired. When processing a webpage, most screen readers either disable CSS (and Javascript) entirely or only process a sub-set, such that things normally hidden by CSS will end up getting processed as though it were visible. Web-devs traditionally exploit this behavior in the name of accessibility by creating a CSS class that can be applied to elements that will hide them from everyone who has CSS enabled, something along the lines of:

.screen-reader-text {
    position: absolute !important;
    height: 1px;
    width: 1px; 
    overflow: hidden;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
    clip: rect(1px, 1px, 1px, 1px);
}

With this class available (your theme’s stylesheets likely have a similar class), inside of your icon span you can add a second, “screen-reader” span that won’t get (or at least appear to be) rendered on-screen but will provide screen readers an accessible alternative to your icon, similar to how the alt attribute works on an <img>:

<span class="icon-shield"><span class="screen-reader-text">Security</span></span>

The above will display your icon when CSS is enabled, and screen readers will interpret “Security” in it’s place.

Give <span> Invisible Content

To prevent the <span>s from getting stripped out, you can add content that isn’t explicitly displayed:

  • Adding a space character between the opening and closing tag will suffice. However, as empty whitespace is stripped you need to add the character via the &nbsp; HTML entity. This character will be converted to appear to be a normal space once you switch tabs or save, however it will not get stripped out of the markup. Note that this may affect the size of your span, just as it would if you placed a single letter in the element:

    <span class="icon-shield">&nbsp;</span>

  • Assuming you are working with a UTF-8 character set, you can use a “soft-hyphen” character via the HTML entity &shy;. Unlike &nbsp;, the soft-hypen character is rendered as though it takes up no space, and shouldn’t affect the size of your span:

    <span class="icon-shield">&shy;</span>

Use an Alternate Element

While WordPress’s TinyMCE is configured to strip out empty <span> elements, there are a number of other tags that are configured by default to allow existing without content. In the comments of ticket #26986, TobiasBg recommends using <i> (<em>, <b>, and <strong> are a few other possibilities). See this Stack Overflow question for more regarding the practice of using <i> for icons.

<i class="icon-shield"></i>

Create Your Own TinyMCE Instance

This is probably the most complicated and generally absurd solution available, and as such I won’t provide an implementation. Using a plugin, it should be possible to load up a separate instance of the TinyMCE scripts, configure them however you desire (beyond the options that WordPress exposes), and replace the default editor (or hide it and add a replacement metabox). While allowing you to disable the removal of empty spans, the amount of time required to flesh out such an implementation would likely greatly outweigh the benefit.

Leave a Comment