Build a Search App: Cleaning Things Up

structure.html

<html>
  <head>
    <title>Instant Search</title>
    <link rel="stylesheet" type="text/css" href="presentation.css" />
  </head>
  <body>
    <p>What's up at <q>Yahoo! Hack Day</q>?
    How about at <q>Madonna</q>'s place?</p>
    <div id="searchMe">
      <input />
      <dl></dl>
    </div>
    <script type="text/javascript" src="behavior.js"></script>
  </body>
</html>

presentation.css

#searchMe { border: 1px solid #ff0; margin:10px; padding:10px;}
#searchMe dl, #searchMe dl dd, #searchMe dl dt { margin:0; padding:0; }
#searchMe dl { max-height:400px; overflow:auto; }
q { background-color:#ff0; color:#000; cursor:help; }
q:before, q:after { content: ""; }

behavior.js

var SEARCH = function() {
  var $ = {};
  return {
    init : function(searchDiv, autoFill) {
      if (autoFill) {
        var theKids = document.getElementsByTagName(autoFill);
        for (var i = 0; i < theKids.length; i++) {
          theKids[i].onmouseover = function () {
            $.q.value = this.innerHTML;
          };
        }
      }
      $.s = document.getElementById(searchDiv);
      $.q = $.s.getElementsByTagName('INPUT')[0];
      $.q.style.width = '100%';
      $.q.value = '';
      $.b = $.s.getElementsByTagName('BUTTON')[0];
      $.r = $.s.getElementsByTagName('DL')[0];
      $.head = document.getElementsByTagName('head')[0];
      $.lastQuery = '';
      setInterval(SEARCH.doStuff, 1000);
    },
    doStuff : function() {
      if ($.q.value) {
        if ($.q.value != $.lastQuery) {
          $.lastQuery = $.q.value;
          if ($.searchScript) {
            $.head.removeChild($.searchScript);
          }
          $.searchScript = document.createElement('script');
          var url='http://search.yahooapis.com/WebSearchService/V1/';
          url+='webSearch?language=en&appid=SearchHack&output=json';
          url+='&callback=SEARCH.showSearchResults&query='+$.q.value;
          SEARCH.runScript($.searchScript, url);
        }
      } else {
        if ($.lastQuery) {
          $.lastQuery = '';
          $.r.innerHTML = '';
        }
      }
    },
    runScript : function(s, url) {
      s.type ='text/javascript';
      s.charset ='utf-8';
      s.src = url;
      $.head.appendChild(s);
    },

    showSearchResults : function(result) {
      $.r.innerHTML = '';
      if (result.ResultSet) {
        for (var i = 0; i < result.ResultSet.Result.length; i++) {
          var dt = document.createElement('dt');
          var a = document.createElement('a');
          a.href = result.ResultSet.Result[i].Url;
          a.target = '_vu';
          a.innerHTML = result.ResultSet.Result[i].Title;
          a.onmouseover = function() {
            SEARCH.getNextSibling(this.parentNode).style.display = 'block';
          }
          a.onmouseout = function() {
            SEARCH.getNextSibling(this.parentNode).style.display = 'none';
          };
          dt.appendChild(a);
          $.r.appendChild(dt);
          var dd = document.createElement('dd');
          dd.innerHTML = result.ResultSet.Result[i].Summary;
          dd.style.display = 'none';
          dd.style.zIndex = 100 + i;
          $.r.appendChild(dd);
        }
      }
    },
    getNextSibling : function(el) {
      var nextSib = el.nextSibling;
      if (nextSib && nextSib.nodeType != 1) {
        nextSib = nextSib.nextSibling;
      }
      return nextSib;
    }
  };
}();

window.onload = function() {
  SEARCH.init('searchMe', 'Q');
};

Up next: get a beer! We're done!