← Home

Safari's beautiful search tag is broken - let's fix that

Everybody's proprietary darling

Clearly I'm late to the party with this, but hey! :-)

So, first of all, for those of you who've been living under a rock, too, here's what all this chattering is about:

In 2004 Apple added the search input type, a proprietary variant of the input tag to WebCore. Besides the new type there are additional attributes: incremental (features spotlight-like incremental searching), placeholder (is displayed as long as the widget is left alone), autosave and results (both used for a "recent searches" drop-down).

The syntax looks like this:

<input type="search" placeholder="Search" autosave="your.domain.name" results="5" />

And it will render like this (when unfocused/focused):

Safari's search tag tag looks like this

Now, what's so cool about this? Aside from looking gorgeous, it adds value in terms of an enhanced user-experience. The usage patterns here are well known, e.g. from browser toolbar search boxes.

So why not just use it? The problem, of course, is that this HTML extension is proprietary. It won't validate, but throw up to four ugly warnings:

Safari's search tag validation fails

Does this really need to be fixed?

When I redesigned my blog the other day and I included this search tag into the layout, almost immediately a friend of mine came up with a blunt "Hey dude, your markup doesn't validate any more." Oh, my gosh! :)

There are reasonable points about using non-standard-compliant, proprietary extensions. For example Random Genius phrased it like this:

<!--
  I know that using type="search" means that my page doesn't validate,
  but for safari users it offers such a nice search experience, and degrades
  nicely for other browsers, that there really is no reason why I shouldn't.
-->

... and even Zeldman seems to agree with Matt's opinion that "there is nothing wrong with proprietary extensions from Apple, Microsoft, whoever as long as the standards are also supported".

But on the other hand we don't like breaking stuff superfluously either, do we? There's something warm, fuzzy and satisfying about seeing this in Safari's status bar:

Safari's status bar, no validation errors

... and to me that's worth a little extra work. So let's see what can be done with some simple, unobtrusive Javascript and CSS.

Check this out

We're going to:

  • use a perfectly standard-conform default text input as a search field and give it a class named "search"
  • hook into the page load event, find the text input and
  • turn it into a Safari search input if Safari is looking at it
  • fake the look of the text field if another browser is looking at it

For the impatient - here's a simple, working demo of this stuff (be sure to try it in Safari and e.g. Firefox):

Demo

Ok, let's start with an input tag like this:

<input type="text" class="search" name="q" />

Next we need a way to hook into the page's onload event. Assuming that we've already loaded Prototype.js we'll use this library register the event handler:

Event.observe(window, 'load', beautify_search_inputs);

Once the page is loaded our beautify_search_inputs function will be called. This function iterates over all input tags in the document that have the class name search (mostly, there will be just one such tag). For each of these tags either the method make_search_input or fake_search_input will be called - depending on the browser type.


function beautify_search_inputs() {
  isSafari = (navigator.userAgent.indexOf("Safari") > 0)
  elements = document.getElementsByClassName('search', null, 'input')
  for (var i = 0; i <elements.length; i++) {
    if (isSafari) {
      make_search_input(elements[i])
    } else {
      fake_search_input(elements[i])
    }
  }
}

That means, if we're on Safari, the input tag will simply be turned into a type="search" input tag:


function make_search_input(element) {
  element.type = 'search'
  element.setAttribute('placeholder', element.value);
  element.setAttribute('autosave', 'my.domain.name');
  element.setAttribute('results', '5');
}

For every other browser we'll at least try to partially fake Safari's look:

To achieve this we'll wrap it into a div, add two more div tags for the round endings (to allow a dynamic adjustment of the width) and hook up event handlers for the focus and blur events (to add that typical glowing blue focus):


function fake_search_input(element) {
  Event.observe(element, 'click', function() {
  	if (element.value == 'Search') element.value = '' })
  Event.observe(element, 'focus', function() {
  	element.parentNode.className = 'search-container search-active' })
  Event.observe(element, 'blur',  function() {
  	element.parentNode.className = 'search-container' })

  container = document.createElement('div')
  container.className = 'search-container'
  container.style.width = element.clientWidth + 'px'
  left = document.createElement('div')
  left.className = 'search-left'
  right = document.createElement('div')
  right.className = 'search-right'

  element.parentNode.insertBefore(container, element)
  element.parentNode.removeChild(element)
  container.appendChild(left)
  container.appendChild(right)
  container.appendChild(element)
}

Of course these three additional div tags are a bit ugly. But I haven't been able to come up with a more elegant way that also dynamically resizes the divs and works in several browsers.

There you have it.

  • Your page validates.
  • Safari users get the full proprietary Apple coolness.
  • All the other major browsers get a nice fake.
  • With Javascript turned of nothing happens at all and the widget "degrades" nicely to its pure input tag form.

How to install

You can find the source here:

http://svn.artweb-design.de/stuff/html/safari_search_tag/

After downloading you'll probably want to integrate this stuff into existing CSS and Javascript files (if you're not using an asset bundler, that is)

Pay attention to either put the image safari_search_tag.png in your /images folder or change the CSS rule in safari_search_tag.css accordingly.

Problems? Anything that can be done better?

Like I said this version relies on Prototype.js. So if you're not using this library, you'll probably need to use a different way to hook up the onload event handler.

Also, if you're able to create the same visual effect in the same browsers with less additional tags, I'd highly appreciate learning about that.

I've checked this in no other browsers than Safari, IE 6.0, IE 7.0, Opera 9.0 and Firefox 2.0.

Feedback?

What do you think?