Previously I've mentioned how I do image searches with my bigger daughter, hunting for images of Cinderella, Ariel the little mermaid, and other equally beautiful princesses. So I thought why not build a custom little app for the little kid to do this, I mean, her, being the good (and beautiful) girl that she is, totally deserves it. Here are the very basics of such an app, my idea was to do a little demo how you can consume Y! services to do web and image searches, using nothing but JavaScript. Look ma, no server! 😉
» demo
JS alone
The cool think about Y! web services is that they provide you different options for the output format of the results they return. You can get XML, php serialized string or JSON. (No SOAP, thank you very much!). I think more companies should consider such an offer, I mean since you get your service to work, the output format is just a ... a format, nothing special.
How do you make the request? With JavaScript includes, meaning you use DOM methods to sreate a new <script> tag and add it to the head
of your page. The src
attribute of the new script tag points to the Y! service, a longish kind of URL with a bunch of parameters.
When making your request, you say that the output format should be JSON. Then you provide a callback function name. What Y! service does is it calls the function you gave, passing the JSON output as a parameter. Let's say you made a request to:
http://..../...&output=json&callback=myfunction
and we assume the result of your request is an array:
[1,2,3]
The Y! service will write out:
myfunction([1,2,3])
At the end, your function gets called, receiving the response from the call to the Y! service. It's now up to this function to decide what to do with the result.
Implementation
It all starts with a simple form and a placeholder for the result:
<body> <form action="" method="get" onsubmit="ysearch.doSearch( this[0].value, 'content', this[1].checked ); return false;"> <input /> <input type="checkbox" />Image search <input type="submit" value="go!"/> </form> <div id="content"> </div> </body>
Submitting this form calls the doSearch()
method of the ysearch
class.
ysearch
class has a few properties:
// url of the image search service this.image_url = "http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?"; // url of the web search service this.web_url = "http://api.search.yahoo.com/WebSearchService/V1/webSearch?"; // ID of element that will // display the result this.where = ''; // is this an image search? this.image_search = false; // parameters for the search this.url = "output=json"+ "&callback=ysearch.process" + "&appid=YahooDemo" + "&start=1" + "&results=5" + "&query="; // reference to the last included script this.last_include = false;
Then comes the doSearch
method. It does the following:
- Checks input parameters to see if this is a request for a web search or an image search. The two types af searches use a different web service with a different URL
- Appends the search query to the URL
- Creates a new
script
tag with SRC that was just figured out - Saves a reference to the new script tag, so that it can be cleaned up with the next search
Here's the actual code for the doSearch()
method:
this.doSearch = function(q, where, web_image) { this.where = where; var script_filename = ""; if (web_image) { script_filename = this.image_url; this.image_search = true; } else { script_filename = this.web_url; this.image_search = false; } script_filename += this.url + escape(q); var html_doc = document.getElementsByTagName('head').item(0); // remove the last included script, we don't need it anymore if (this.last_include) { html_doc.removeChild(this.last_include); } var js = document.createElement('script'); js.setAttribute('type', 'text/javascript'); js.setAttribute('src', script_filename); html_doc.appendChild(js); // set the pointer to the last include this.last_include = js; };
Becuase we passed the callback function name ysearch.process
to the service, the new Y! script will call it, passing the search results as an object. Let's see what process() does, really nothing but looping through the results object (description of the object here and here) and spitting out HTML using the politically incorrect innerHTML. Here's the code:
this.process = function(resp) { if (resp.ResultSet && resp.ResultSet.totalResultsReturned > 0) { // loop through the results var out = ''; for (var i = 0, max = resp.ResultSet.Result.length; i < max; i++) { var item = resp.ResultSet.Result[i]; if (this.image_search) { out += '<p><a href="' + item.Url + '">' out += '<img src="' + item.Thumbnail.Url + '" /><\/a>'; out += '<br />' + item.Summary + '<\/p>'; } else { out += '<p><a href="' + item.Url + '">' + item.Title + '<\/a>'; out += '<br />' + item.Summary + '<\/p>'; } } document.getElementById(this.where).innerHTML = out; } else { alert('no results'); } }