SpiffY!Search--"Semi-Permeable Inline Free-Form Yahoo! Search," if you just can't stand not knowing--uses Yahoo!'s Web Search APIs and Related Suggestion APIs to field search queries and return them in JSON format, wrapped in handy JavaScript callbacks. From there it's a (fairly) trivial exercise to allow user interaction through dynamic script nodes.
SpiffY!Search was my very first (and favorite) inline search thingamabob. While much of it is still solid, it's growing long in the tooth, and is in dire need of an upgrade. It's here because there are a ton of links pointing back to it, and I didn't want to just cut everybody loose in space. Someday When I Have Time™, there will be a re-write.
If you'd like to see what I've been doing recently around inline badges, please check out Case-Hardened JavaScript.
To get SpiffY!Search for your site, click the bit that says "get this for your site." Which, concidentally, will bring you right back here.
You'll want to save the structure, behavior, and presentation into three separate files on your desktop. Drag the structure file into your browser, and you ought to see something working immediately.
<div id="ss">SpiffY!Search:
<input value="">
<select></select>
<input type="checkbox" class="c">
<input>
<dl>
<dt class="nav">
<a class="dir prev">prev</a> <a>Powered by Yahoo! Search</a> <a class="dir next">next</a>
</dt>
<dt class="also"></dt>
<dt class="help">
<a>close results</a> - <a>get this for your site</a>
</dt>
</dl>
</div>
var KENTBREWSTER = window.KENTBREWSTER || {};
KENTBREWSTER.ss = function() {
var $ = {
bouncePass : function(v) {
var v = v || window.event;
if (v.target) {
var el = (v.target.nodeType == 3) ? v.target.parentNode : v.target;
}
else {
var el = v.srcElement;
}
if (el.bounce) {
eval($.selfName + "." + el.bounce + "(v, el)");
}
}
};
return {
init : function(selfObj, homeDiv, autoFillTag, defaultDomain, defaultDomainRestrict) {
$.selfName = this.getSelfName(selfObj);
if (autoFillTag) {
theKids = document.getElementsByTagName(autoFillTag);
for (var i=0; i<theKids.length; i++)
{
theKids[i].onmouseover = function () { eval($.selfName + '.stuffQuery(this)'); };
}
}
$.lastQuery = '';
$.newDomain = '';
$.language = 'en';
$.resultStart = 0;
$.resultsPerPage = 10;
$.appId = 'SpiffySearch';
$.head = document.getElementsByTagName('head')[0];
$.homeDiv = document.getElementById(homeDiv);
$.mode = $.homeDiv.getElementsByTagName('SELECT')[0];
$.mode.selectedIndex = 0;
var input = $.homeDiv.getElementsByTagName('INPUT');
$.result = $.homeDiv.getElementsByTagName('DL')[0];
$.result.style.top = $.homeDiv.offsetHeight + 3 + 'px';
$.result.restore = $.result.innerHTML;
$.tag = ['The Web', 'News', 'Video', 'Images', 'MyWeb', 'Podcast'];
$.service = [
'http://api.search.yahoo.com/WebSearchService/V1/webSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&query=',
'http://api.search.yahoo.com/NewsSearchService/V1/newsSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&query=',
'http://api.search.yahoo.com/VideoSearchService/V1/videoSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&query=',
'http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&query=',
'http://api.search.yahoo.com/MyWebService/V1/urlSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&tag=',
'http://api.search.yahoo.com/AudioSearchService/V1/podcastSearch?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongSearch&query='
]
$.home = [
'http://search.yahoo.com/search?p=',
'http://news.search.yahoo.com/search/news?p=',
'http://video.search.yahoo.com/search/video?p=',
'http://images.search.yahoo.com/search/images?p=',
'http://myweb.yahoo.com/myresults/ourresults?dmode=oursrch&p=',
'http://audio.search.yahoo.com/search/audio?stype=pod&p='
]
$.relatedService = 'http://api.search.yahoo.com/WebSearchService/V1/relatedSuggestion?language=' + $.language + '&appid=' + $.appId + '&output=json&callback=' + $.selfName + '.pongRelated&query=';
for (var i=0; i<$.service.length; i++) {
var option = document.createElement('OPTION');
option.innerHTML = $.tag[i];
$.mode.appendChild(option);
}
$.mode.onchange = function () {
$.lastQuery = '';
$.resultStart=0;
}
$.query = input[0];
$.query.value = '';
$.restrict = input[1];
$.restrict.checked = defaultDomainRestrict;
$.restrict.onmouseup = function () { $.lastQuery = ''; $.resultStart=0; };
$.restrict.title = 'Restrict to ' + defaultDomain;
$.domain = input[2];
$.domain.onblur = function () {
$.newDomain = $.domain.value;
$.resultStart = 0;
$.lastQuery = '';
}
$.domain.title = 'Click to edit!';
$.domain.value = defaultDomain;
$.newDomain = $.domain.value;
setInterval($.selfName + ".pingSearch()", 500);
},
resetResults : function() {
$.result.innerHTML = $.result.restore;
var link = $.result.getElementsByTagName('A');
var dt = $.result.getElementsByTagName('DT');
$.navBar = dt[0];
$.navBar.style.whiteSpace = 'normal';
$.relatedResults = dt[1];
$.prevLink = link[0];
$.prevLink.onmouseup = function () { eval($.selfName + '.prevSet()'); };
$.prevLink.title = 'previous ' + $.resultsPerPage + ' results';
$.homeLink = link[1];
$.homeLink.onmouseup = function () { eval($.selfName + '.prevSet()'); };
$.homeLink.title = 'Yahoo! Search';
$.nextLink = link[2];
$.nextLink.onmouseup = function () { eval($.selfName + '.nextSet()'); };
$.nextLink.title = 'next ' + $.resultsPerPage + ' results';
$.closeThis = link[3];
$.closeThis.onmouseup = function () { eval($.selfName + '.closeResults()'); };
$.helpLink = link[4];
$.helpLink.url = 'http://kentbrewster.com/spiffysearch';
$.helpLink.bounce = 'goThere';
$.helpLink.onmouseup = $.bouncePass;
},
pingSearch : function() {
if (!$.query.value && $.lastQuery) {
this.closeResults();
return false;
}
if ($.query.value && ($.lastQuery != $.query.value)) {
// if this is true, query has changed
if ($.lastQuery) {
$.resultStart = 0;
}
if ($.newDomain != $.domain.value) {
$.domain.value = $.newDomain;
}
$.lastQuery = $.query.value;
if ($.searchScript) {
this.removeScript($.searchScript);
}
if ($.relatedScript) {
this.removeScript($.relatedScript);
}
$.searchScript = document.createElement('script');
var url = $.service[$.mode.selectedIndex] + $.query.value;
if ($.resultStart) {
url += '&start=' + $.resultStart;
}
if ($.restrict.checked) {
url += '&site=' + $.domain.value;
$.restrict.title = 'Show all results.'
} else {
$.restrict.title = 'Restrict to ' + $.domain.value;
}
this.runScript($.searchScript, url);
}
},
pongSearch : function(z) {
this.resetResults();
$.result.style.display = 'block';
if ($.resultStart) {
$.prevLink.style.display = 'block';
} else {
$.prevLink.style.display = 'none';
}
$.homeLink.url = $.home[$.mode.selectedIndex] + $.query.value + '&b=' + $.resultStart;
$.homeLink.bounce = 'goThere';
$.homeLink.onmouseup = $.bouncePass;
if (z.ResultSet.totalResultsAvailable > $.resultStart + $.resultsPerPage) {
$.nextLink.style.display = 'block';
} else {
$.nextLink.style.display = 'none';
}
for (var i = 0; i < z.ResultSet.Result.length; i++) {
var dt = document.createElement('dt');
if (i % 2) {
dt.className = 'odd';
}
dt.style.whiteSpace = 'nowrap';
var a = document.createElement('a');
a.innerHTML = '@';
a.title = 'Add to MyWeb';
a.className = 's';
a.onmouseup = function() { eval($.selfName + '.saveBookmark(this)'); };
dt.appendChild(a);
dt.appendChild(document.createTextNode(' '));
var a = document.createElement('a');
a.url = z.ResultSet.Result[i].Url;
a.innerHTML = z.ResultSet.Result[i].Title;
a.bounce = 'goThere';
a.onmouseup = $.bouncePass;
a.onmouseover = function() {
eval($.selfName + '.showDetails(this)');
};
a.onmouseout = function() {
eval($.selfName + '.hideDetails(this)');
};
dt.appendChild(a);
$.result.insertBefore(dt, $.relatedResults);
var dd = document.createElement('dd');
dd.style.display = 'none';
dd.style.zIndex = 100 + i;
var h4 = document.createElement('h4');
h4.innerHTML = z.ResultSet.Result[i].Title;
dd.appendChild(h4);
dt.appendChild(document.createTextNode(' '));
if (z.ResultSet.Result[i].Thumbnail) {
var img = document.createElement('img');
img.src = z.ResultSet.Result[i].Thumbnail.Url;
img.align = 'right';
img.height = z.ResultSet.Result[i].Thumbnail.Height;
img.width = z.ResultSet.Result[i].Thumbnail.Width;
dd.appendChild(img);
}
var p = document.createElement('p');
p.innerHTML = z.ResultSet.Result[i].Summary;
dd.appendChild(p);
var p = document.createElement('p');
p.innerHTML = 'Showing all results. Shift-click to restrict results to ' + this.getDomain(a.url);
if ($.restrict.checked) {
p.innerHTML = 'Results are restricted to ' + $.domain.value + '. Shift-click to show all results.';
}
dd.appendChild(p);
$.result.insertBefore(dd, $.relatedResults);
}
if (!z.ResultSet.Result.length) {
var dt = document.createElement('dt');
dt.className = 'msg';
var msg = 'Nothing found, sorry.';
if ($.restrict.checked && $.domain.value) {
msg += ' Uncheck the Restrict box to try the whole Web.';
}
dt.appendChild(document.createTextNode(msg));
$.result.insertBefore(dt, $.relatedResults);
}
$.relatedResults.style.display = 'none';
if (!$.resultStart) {
this.pingRelated();
}
else {
if ($.relatedResults) {
$.relatedResults.style.display = 'block';
}
}
},
nextSet : function() {
$.resultStart = $.resultStart + $.resultsPerPage;
$.lastQuery = '';
},
prevSet : function() {
$.resultStart = $.resultStart - $.resultsPerPage;
$.lastQuery = '';
},
pingRelated : function()
{
if ($.relatedScript) {
this.removeScript($.relatedScript);
}
$.relatedScript = document.createElement('script');
this.runScript($.relatedScript, $.relatedService + $.query.value);
},
pongRelated : function(z)
{
$.relatedResults.innerHTML = '';
if (z.ResultSet.Result)
{
if (z.ResultSet.Result.length)
{
$.relatedResults.appendChild(document.createTextNode('See Also: '));
for (var i = 0; i < z.ResultSet.Result.length; i++)
{
if (i)
{
$.relatedResults.appendChild(document.createTextNode(', '));
}
var a = document.createElement('a');
a.innerHTML = z.ResultSet.Result[i];
a.onmouseup = function () { eval($.selfName + '.stuffQuery(this)'); };
a.title = 'Click to search for ' + z.ResultSet.Result[i];
$.relatedResults.appendChild(a);
}
$.relatedResults.style.display = 'block';
}
}
},
goThere : function(event, element) {
if (event.shiftKey) {
$.restrict.click();
$.lastQuery = '';
$.resultStart = 0;
$.newDomain = eval($.selfName + '.getDomain(element.url)');
} else {
window.open(element.url, '_blank', '', 0);
}
},
saveBookmark : function(el) {
var ns = this.getNextSibling(el);
var url = 'http://myweb2.search.yahoo.com/myresults/bookmarklet?t=';
url += escape(ns.innerHTML);
url += '&u=';
url += escape(ns.url);
url += '&tag=';
url += escape($.query.value);
url += '&ei=UTF-8';
window.open(url,'popup','width=600px,height=420px,status=0,location=0,resizable=1,scrollbars=1,left=100,top=50',0);
},
getDomain : function(url) {
var nameSpace = url.split('?')[0].split('//')[1].split('/')[0].split('.');
var domain = nameSpace[nameSpace.length-2] + '.' + nameSpace[nameSpace.length-1];
if ((nameSpace[nameSpace.length-1].length) == 2) {
domain = nameSpace[nameSpace.length-3] + '.' + domain;
}
return domain;
},
stuffQuery : function(el) {
$.resultStart = 0;
$.query.value = el.innerHTML;
},
closeResults : function() {
$.result.style.display = 'none';
$.query.value = '';
$.lastQuery = '';
},
showDetails : function(el) {
this.getNextSibling(el.parentNode).style.display = 'block';
},
hideDetails : function(el) {
this.getNextSibling(el.parentNode).style.display = 'none';
},
getNextSibling : function(el) {
var nextSib = el.nextSibling;
if (nextSib && nextSib.nodeType != 1) {
nextSib = nextSib.nextSibling;
}
return nextSib;
},
runScript : function(s, url) {
s.type ='text/javascript';
s.charset ='utf-8';
s.src = url;
$.head.appendChild(s);
},
removeScript : function(s) {
try {
$.head.removeChild(s);
} catch(e) {}
},
getSelfName : function(selfObj) {
var s = document.createElement('SPAN');
s.innerHTML = selfObj;
var nameSpace = s.innerHTML.split('{')[1].split('(')[0].replace(/^\s+/, '').split('.');
var selfName = '';
for (var i = 0; i < nameSpace.length - 1; i++) {
if (selfName) {
selfName += '.';
}
selfName += nameSpace[i];
}
return selfName;
}
};
}();
window.onload = function() {
KENTBREWSTER.ss.init(arguments.callee, 'ss', 's', 'kentbrewster.com', false);
};
#ss { float:right; margin:10px; color:#fff; padding:5px; background-color:#000; position:relative;}
#ss input { background: #ffc; border:1px solid #000; font-size:92%; margin:2px;}
#ss input.c { border: none; margin:0; padding:0; background:transparent;}
#ss a { color:#fff; }
#ss dl { display:none; position:absolute; top:0; right:0; z-index: 3;}
#ss dl dd { position:absolute; left:-20px; overflow:hidden; display:none; background:#fff; color:#000; border: 1px solid #000; width:100%; margin: 5px;}
#ss dl dd * { margin:5px; padding:0; }
#ss dl dt { background:#fff; padding:2px; width:320px; overflow:hidden; border-right:1px solid black; border-left: 1px solid black; }
#ss dl dt a { color:#000; }
#ss dl dt.odd { background-color:#fee;}
#ss dl dt.also { background-color:#fcc; color:#000;}
#ss dl dt.msg { color:#000; }
#ss dl dt.nav { text-align:center; position:relative; background-color:#300; }
#ss dl dt.nav a { color:#fff; }
#ss dl dt.nav a.dir { position:absolute; top:2px; }
#ss dl dt.nav a.prev { left: 5px; }
#ss dl dt.nav a.next { right: 5px; }
#ss dl dt.help { text-align:right;background-color:#300; color:#fff; }
#ss dl dt.help a { color:#fff; }
s { text-decoration:none; background-color:#ffa; border-bottom:1px dashed purple; cursor:pointer; }