8

When I use the WordPress HTML editor to add icomoon icons like this:

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

and then switch to the visual editor and then back to the HTML editor, the icons disappear. I am trying to include this bit of Javascript to fix this. I looked at wp_enqueue_scripts and put this in functions.php, but it still doesn't fix the problem.

function oakwood_tinymcefix() {
    wp_enqueue_script( 'tiny_mce' );
    echo '<script type="text/javascript">'
   , 'tinymce.init({
            ...
            extended_valid_elements : "img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name]"
        });'
   , '</script>';
}
add_action( 'admin_enqueue_scripts', 'oakwood_tinymcefix' );

How can I fix this?

  • For starters, ellipses ... are not valid Javascript, and are frequently used to indicate the omission of content. In this instance, I believe the author of the doc intended for you to supply your own TinyMCE configuration where the ellipsis is present. Furthermore, you've used the default value from the docs which explicitly states it replaces TinyMCE's img rule - this does nothing for your span problem. You have a better chance of receiving a valid answer the more research effort you display - please spend some more time with the TinyMCE docs. – bosco Jun 2 '14 at 17:09
  • In particular, the valid_elements option documentation should help you figure out what you should be passing to the extended_valid_elements option. I haven't read the docs, but it will probably look pretty similar to "span[class|id|title|style]" – bosco Jun 2 '14 at 17:12
  • Thanks. I don't know Javascript, so I just assumed that snippet would work. My bad. – mcography Jun 2 '14 at 17:12
  • 1
    No offense, but you really, really should not be using code that you do not understand. That is a hacker's dream isn't it? Get a user to install code (that just happens to be malicious). – s_ha_dum Jun 2 '14 at 18:18
  • After some research, this issue appears to be somewhat more complex than I initially thought. I will add my findings and any workarounds/solutions later today. Aside: @s_ha_dum brings up a very valid point. If you plan to spend some time working with tweaking/developing on/for WordPress in any capacity, you should spend some time familiarizing yourself with it's foundational techs - even a basic understanding can go a long way towards helping you troubleshoot and ask good questions, not to mention keeping your site secure. – bosco Jun 2 '14 at 18:25
8

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.

  • Thanks a million, @boscho! I really appreciate you taking the time to help me out. I have to sign off now, but I am going to look into your suggestions later tonight. – mcography Jun 2 '14 at 22:04
  • <i> elements are stripped out as well. Seems that only aren't stripped. – EHerman Jan 3 '15 at 17:03
  • @EHerman I just performed some experiments, and every empty i element down to the simplest <i></i> was retained over the course of a new post's creation and subsequent modification - none were stripped out of the content. Nor can I identify such a behavior in the core codebase. Could you provide a use case for WordPress stripping empty i elements? – bosco Jan 31 '15 at 0:33
  • Another method to get around this would be to create a shortcode that inserts the icon into the content. Something like [icon class=shield]. This makes the icon also editable while using the visual editor. – kuchenundkakao Aug 31 '17 at 10:13
  • 1
    Another alternative is to put a HTML comment within the span element like <span class="icon-shield"><!-- icon --></span>. This takes up no space like &nbsp; does and prevents TinyMCE from removing the element. – j08691 Aug 27 '18 at 13:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.