views:

483

answers:

3

So I'm scripting up a search field for my website. As the user types, a box appears below the field with search suggestions. The suggestions are keyboard navigable and clickable.

However, I'm having trouble getting the suggestions to disappear when the user click's elsewhere on the page. I've added an onBlur() handler to the search field, but when the user clicks on one of the suggestions, the blur happens before the click, thus making the suggestions box disappear and the click event happens in dead space instead of on the suggestion.

Here's what my HTML looks like:

<input type="text" id="search_field" onBlur="hideSuggestions()" />
<div id="suggestions_box">
  <ul>
    <li onClick="doSomething(1)">suggestion1</li>
    <li onClick="doSomething(2)">suggestion2</li>
  </ul>
</div>

So the question is, how can I hide the suggestions_box element when the focus leaves the search field, except when the new element with focus is the suggestions box?

+1  A: 

Keep your suggestions box div inside your form tag, then put the onBlur on the form.

<form id="myform" onBlur="hideSuggestions()" >
    <input type="text" id="search_field" />
    <div id="suggestions_box">
        <ul>
          <li onClick="doSomething(1)">suggestion1</li>
          <li onClick="doSomething(2)">suggestion2</li>
        </ul>
    </div>
 </form>

[edit] I'm thinking this will work, but can't test it right now. The javascript reference over at w3schools says that form tag supports onblur, but I don't know if that means it has to be on a form element, or just within the form tag.[/edit]

idrumgood
A: 

Try this.

First make sure you pass the blur event to your hideSuggestions function:

<input type="text" id="search_field" onblur="hideSuggestions(event)" />

Next, check if the blur was caused by an event inside the suggestions box:

function hideSuggestions(event) {
    var target = event.relatedTarget || event.toElement,
        suggestions = document.getElementById('suggestions_box');

    if (contains(suggestions, target))
        // user did something inside suggestions box.
        return; // Do nothing.

    // box hiding code as before...
}

// contains(haystack, needle): fast lookup of whether haystack is a parent of needle.
var contains = document.compareDocumentPosition
             ? function(a, b) { return !!(a.compareDocumentPosition(b) & 16) }
             : function(a, b) { return a !== b && (a.contains ? a.contains(b) : true) };
Crescent Fresh
Thanks. I think this is the right solution in general, but I'm using jquery, which doesn't correctly set the relatedTarget property (it's always undefined). Any workaround ideas?
Aaron
I believe jQuery populates `relatedTarget` in general. However I don't know enough about the `blur` event and how jQuery interprets the `toElement` in IE. That may be your problem.
Crescent Fresh