IE8 ghost :after and :before pseudo elements

IE8 has a strange bug (what bugs aren't strange in IE ?) when dealing with :after and :before pseudo-elements.

I was adding a nice looking arrow after one of my elements using :after. I wanted this arrow to only display when my element was hovered, so I wrote the following code :

<a href="#">Example</a>
a {
  position:relative;
  display:block;
  height:30px;
}
a:after {
  position:absolute;
  content:"";
  top:0px;
  right:-15px;
  width:15px;
  height:30px;
  background:url(arrow.gif) top left no-repeat;
  display:none;
}
a:hover:after {
  display:block;
}

As you can see, nothing too fancy. I positionned my arrow using an empty :after element and a backgroundimage. I defaulted the arrow to hidden, and only show it when hovering the element.

IE in action

It does work pretty well in moder browsers. It also seems to work on IE8. When you hover the element in IE8, the arrow gets displayed. But it does not gets hidden when you stop hovering it.

There's a kind of ghost element that keeps getting displayed. It gets removed if you directly mouse it, or scroll your page, or alt-tab, etc. This clearly is a display artefact.

To counter this I had to write it in an other fashion (less readable in my opinion). Removing the default a:after rule and adding all properties to a:hover:after :

a {
  position:relative;
  display:block;
  height:30px;
}
a:hover:after {
  position:absolute;
  content:"";
  top:0px;
  right:-15px;
  width:15px;
  height:30px;
  background:url(arrow.gif) top left no-repeat;
  display:block;
}

Update

It should be noted that more generally, I gets confused and create ghost elements and styling when we try to update the :after/:before properties based on a rule selecting its parent.

There seems to have a little lag/delay before the properties gets applied, and most of the time they do not.

Firing cakePHP components callbacks on error404

I often use $this->cakeError('error404') in my controllers to stop processing the request (and return a 404 error) when the supplied parameters are buggy or incomplete.

A strange side effect

I recently spotted a strange side effect on the latest site I was developping. It used a pretty large html footer filled with links dynamically fetched from my database.

As all pages (no matter what model/controller they were refering to) were using the same footer, I created a component with a simple startup method to fetch them all and return them to the view. I added this component to my AppController so that every controller will inherit it.

This worked nicely until I spotted that on error pages, the footer was left mostly empty. I was because my Component callback was never fired.

How's that ?

When detecting an error, cake starts using its ErrorHandlerand thus do not fire callbacks.

Fortunatly, you can create an AppErrorclass (in app/app_error.php) and overwrite the ErrorHandlermethod. Namely, the error404. I rewrote mine to explicitly fire the initialize and startup methods.

class AppError extends ErrorHandler {
  function error404($params) {
    $this->controller->Component->initialize($this->controller);
    $this->controller->Component->startup($this->controller);
    parent::error404($params);
  }
}

I only fired two of the callbacks, but maybe beforeRenderand shutdownshould be fired too.

Moving from Delicious to Pinboard

As you may have heard, Yahoo! is closing Delicious. I had started to use Delicious a lot lately, to work as a decentralised save of all my bookmarks and being able to search through them easily.

In my recent attempt to save as much of my work online, losing Delicious would meant losing a large part of collective wisdom. So I search for an alternative.

I finally registered for Pinboarda few days ago. It allows an easy way to import Delicious bookmarks in a matter of seconds and has all the features of Delicious I wanted.

It is not free however, but the one-time fee depends on the number of people using the service. I payed mine 9.12$.

The website itself is not as good looking as Delicious (ahem), but it does provide a cleaner search experience (in my point of view). I really don't care about other people bookmark, I only search through mine.

Detecting fullscreen in tinyMCE

I wanted one of my tinyMCE plugins to fire a certain set of actions whenever the full screen mode was activated.

I search for an onFullScrenevent or somethin similar but to no avail. I finally discovered that the full screen actually creates a whole new tinymce editor instance.

So the only thing I had to do was writing my little piece of code in the initmethod of my plugin and check to see if the fullscreen was enabled.

The initial fullscreen plugin exposes a fullscreen_is_enabled param that can be checked.

All I had to do was a simple condition like : if (editor.getParam('fullscreen_is_enabled')) {}. And once in the condition I tried to execute my custom code. I needed the editor.execCommand method but all I got was an error because t.selection was not set.

It appears that the execCommand can only be executed when the full editor instance is loaded (and thus a t.selection is created). So I wrap my little piece of code into a setInterval checking for editor.selection and finally executed my code when the .selectionwas set.

Here is my final snippet, to be included in my plugin initmethod :

if (editor.getParam('fullscreen_is_enabled')) {
  var fullScreenInterval = setInterval(function() {
    if (!editor.selection) return false;
    clearInterval(fullScreenInterval);
    editor.execCommand('anyTinyMCECommand');
  }, 1000);
}

Adding a <hr> in a tinyMCE instance

When using the default tinyMCE implementation to add an <hr> element to the editor content, the <hr> is added inside its parent <p> element (when it should be an element on its own, without such a parent).

I've added my own plugin to resolve this small issue, here is the code :

(function() {
  tinymce.create('tinymce.plugins.pixelastic_hrPlugin', {
    init : function(editor, url) {
      // Register the command
      editor.addCommand('mcepixelastic_hr', function() {
        // We get the parent node
        var parentNode = editor.selection.getNode(),
        uniqueId = editor.dom.uniqueId();
        // We insert the hr (with a unique id to select it later)
        editor.execCommand('mceInsertContent', false, '<hr id="'+uniqueId+'" />');
        var hr = editor.dom.select('#'+uniqueId)[0];
        // We split the parent element around the hr
        editor.dom.split(parentNode, hr);
        // We remove the temporary id
        $(hr).attr('id', null);
      });
      // Adding a button
      editor.addButton(pluginName, { title : 'pixelastic_hr.desc', cmd : 'mcepixelastic_hr' });
      }
    }
  });
  // Register plugin
  tinymce.PluginManager.add('pixelastic_hr', tinymce.plugins['pixelastic_hrPlugin']);
})();

The trick to use the editor.dom.split method to split the parent element around the <hr> element.

The dirty hacks is that there is no way to get a direct reference to a DOM element added through mceInsertContent, so we need to set a temporary unique id and then select it through this id.

I use jQuery in my example and I strongly suggest you to do the same, the selectors it provides are much sexier and helps writing these kind of plugins pretty fast.