<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:series="http://unfoldingneurons.com/"
><channel><title>David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞. &#187; AJAX</title> <atom:link href="http://davidwalsh.name/tutorials/ajax/feed" rel="self" type="application/rss+xml" /><link>http://davidwalsh.name</link> <description>Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</description> <lastBuildDate>Sat, 04 Feb 2012 19:28:58 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3</generator> <item><title>PHP Advent 2011:  Cross-Origin Ajax with&#160;CORS</title><link>http://davidwalsh.name/ajax-cors</link> <comments>http://davidwalsh.name/ajax-cors#comments</comments> <pubDate>Fri, 16 Dec 2011 15:47:53 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5345</guid> <description><![CDATA[I&#8217;ve had the honor of writing for this year&#8217;s PHP Advent, blessing you all about Cross-Origin Requests with CORS: There’s no doubt that Ajax is one of the most exciting, useful, and necessary web technologies available to front-end developers. Unfortunately, it’s also one of the most restrictive — especially when it comes to gathering content from other [...]<p><a
href="http://davidwalsh.name/ajax-cors">PHP Advent 2011:  Cross-Origin Ajax with&nbsp;CORS</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<img
src="http://davidwalsh.name/dw-content/pcworld_xmas_small.jpg" alt="Cross-Origin Ajax with CORS" class="image" /><p>I&#8217;ve had the honor of writing for this year&#8217;s PHP Advent, blessing you all about Cross-Origin Requests with CORS:</p><blockquote><p>There’s no doubt that Ajax is one of the most exciting, useful, and necessary web technologies available to front-end developers. Unfortunately, it’s also one of the most restrictive — especially when it comes to gathering content from other domains. Web developers are nothing if not persistent, so we’ve come up with a variety of ways to get around cross-origin restrictions, including JSONP, server-side proxies made with PHP, ProxyPass proxying, Flash transports, creative iFrame uses, and more. What many developers don’t know is that there’s a W3C specification called Cross-Origin Resource Sharing, or CORS, which provides a standard for cross-origin Ajax requests with minimal hassle.</p></blockquote><div
class="actions"><a
href="http://phpadvent.org/2011/cross-origin-ajax-with-cors-by-david-walsh" rel="nofollow" class="demo">View Post</a><div
class="clear"></div></div><p>That&#8217;s my Christmas gift to you all!</p><p><a
href="http://davidwalsh.name/ajax-cors">PHP Advent 2011:  Cross-Origin Ajax with&nbsp;CORS</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/ajax-cors/feed</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>JavaScript Coding with&#160;Class</title><link>http://davidwalsh.name/javascript-coding-class</link> <comments>http://davidwalsh.name/javascript-coding-class#comments</comments> <pubDate>Thu, 15 Sep 2011 15:08:16 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[Dojo]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5291</guid> <description><![CDATA[I&#8217;ve spent the last two weeks in London, eating fish&#8217;n'chips, drinking cup&#8217;o'tea, and being a hooligan at the Arsenal. Oh yeah, there was a MooTools hackathon too. The MooTools hackathon was hugely successful and I&#8217;ll be providing more detail about what was accomplished and where MooTools is going over the coming weeks. It was also [...]<p><a
href="http://davidwalsh.name/javascript-coding-class">JavaScript Coding with&nbsp;Class</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<a
href="http://skillsmatter.com/podcast/ajax-ria/ajax-mootools-dojo" rel="nofollow"><img
src="http://davidwalsh.name/dw-content/london_ajax_logo.gif" class="image" alt="London Ajax" /></a><p>I&#8217;ve spent the last two weeks in London, eating fish&#8217;n'chips, drinking cup&#8217;o'tea, and being a hooligan at the Arsenal.  Oh yeah, there was a MooTools hackathon too.  The MooTools hackathon was hugely successful and I&#8217;ll be providing more detail about what was accomplished and where MooTools is going over the coming weeks.  It was also great to meet some of the development team in person instead of simple IRC.  MooTools FTW!</p><p>Another exciting part of my time in London was presenting at London Ajax.  My presentation was called &#8220;JavaScript Coding with Class&#8221;, preaching the values of class-based JavaScript frameworks like MooTools and Dojo.  I kept the talk high-level but I&#8217;m confident I got my point across, showing the value of class structures.</p><div
class="actions"><a
href="http://skillsmatter.com/podcast/ajax-ria/ajax-mootools-dojo" class="demo" rel="nofollow">Watch Presentation</a><div
class="clear"></div></div><p>This was my first time presenting this deck, so let me know if you see room for improvement (outside of the billion &#8220;um&#8217;s&#8221; I used.)</p><div
style="width:425px" id="__ss_9275189"> <strong
style="display:block;margin:12px 0 4px"><a
href="http://www.slideshare.net/davidwalsh83/javascript-coding-with-class" title="JavaScript Coding with Class" target="_blank">JavaScript Coding with Class</a></strong> <iframe
src="http://www.slideshare.net/slideshow/embed_code/9275189" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe><div
style="padding:5px 0 12px"> View more <a
href="http://www.slideshare.net/" target="_blank">presentations</a> from <a
href="http://www.slideshare.net/davidwalsh83" target="_blank">davidwalsh83</a></div></div><p><em>Due to popular request, my slides have been embedded above.</em></p><p><a
href="http://davidwalsh.name/javascript-coding-class">JavaScript Coding with&nbsp;Class</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/javascript-coding-class/feed</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Custom AJAX Content Handling with the Dojo&#160;Toolkit</title><link>http://davidwalsh.name/dojo-xhr-handleas</link> <comments>http://davidwalsh.name/dojo-xhr-handleas#comments</comments> <pubDate>Wed, 17 Aug 2011 14:13:44 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[Dojo]]></category> <category><![CDATA[JavaScript]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5283</guid> <description><![CDATA[If you were to ask me for the top five words that should describe any JavaScript framework, one of them would be flexible.  The Dojo Toolkit is ultra-flexible in just about every way, using customizable classes and dojo-namespaced objects to to allow for maximal flexibility.  One of those dojo-namespaced objects, dojo.contentHandlers, is an object containing [...]<p><a
href="http://davidwalsh.name/dojo-xhr-handleas">Custom AJAX Content Handling with the Dojo&nbsp;Toolkit</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p>If you were to ask me for the top five words that should describe any JavaScript framework, one of them would be flexible.  The Dojo Toolkit is ultra-flexible in just about every way, using customizable classes and <code>dojo</code>-namespaced objects to to allow for maximal flexibility.  One of those <code>dojo</code>-namespaced objects, <code>dojo.contentHandlers</code>, is an object containing key-&gt;value pairs for handling the result of AJAX requests.  Let me show you how to use these content handlers and how you can create your own!</p><h2><code>dojo.xhr</code> and&nbsp;<code>handleAs</code></h2><p>Making AJAX requests is done with Dojo&#8217;s <code>dojo.xhr</code> methods.  Sending a basic GET request would look like:</p><pre class="js">
dojo.xhrGet({
	url: "/ajax.php",
	load: function(result) {
		// Do something with the result here
	}
});
</pre><p>The request above assumes that the response should be handled as plain text, as you would expect.  Dojo&#8217;s <code>dojo.xhr</code> methods all accept an object with properties for handling the request, and one property you can add is <code>handleAs</code>.  The <code>handleAs</code> property should be a string representing the type of parsing that should be done to the result before its passed to the <code>load</code> method or deferred callback.  Values for the handleAs property could be json, javascript, xml, or other variants of json.  If I want my request to be handled as JSON, I&#8217;d code:</p><pre class="js">
dojo.xhrGet({
	url: "/ajax.php",
	handleAs: "json",
	load: function(result) { // result is a JS object
		// Do something with the result here
	}
});
</pre><p>The resulting object provided to the load handler is text parsed into JavaScript object.  Likewise, if I want the result to be handled as XML, I&#8217;d code:</p><pre class="js">
dojo.xhrGet({
	url: "/ajax.php",
	handleAs: "xml",
	load: function(result) { // result is a XMLDocument object
		// Do something with the result here
	}
});
</pre><p>The load callback is provided a <code>XMLDocument</code> object.  One simple parameter changes the way the request response is parsed.  So how is this possible, and how can you create custom <code>handleAs</code> methods?  Simple!</p><h2>dojo.contentHandlers</h2><p>The <code>dojo.contentHandlers</code> object acts as dictionary for ajax request parsing.  The handleAs parameter you  supply maps to the key within <code>dojo.contentHandlers</code>.  The <code>dojo.contentHandlers</code> object comes with the following content handlers:  javascript, json, json-comment-filtered, json-comment-optional, text, and xml.  Here&#8217;s a snippet containing those &#8220;parsers&#8221;:</p><pre class="js">
var handlers = dojo._contentHandlers = dojo.contentHandlers = {

	text: function(xhr){ 
		// summary: A contentHandler which simply returns the plaintext response data
		return xhr.responseText; 
	},
	json: function(xhr){
		// summary: A contentHandler which returns a JavaScript object created from the response data
		return _d.fromJson(xhr.responseText || null);
	},
	"json-comment-filtered": function(xhr){ 

		if(!dojo.config.useCommentedJson){
			console.warn("Consider using the standard mimetype:application/json."
				+ " json-commenting can introduce security issues. To"
				+ " decrease the chances of hijacking, use the standard the 'json' handler and"
				+ " prefix your json with: {}&#038;&#038;\n"
				+ "Use djConfig.useCommentedJson=true to turn off this message.");
		}

		var value = xhr.responseText;
		var cStartIdx = value.indexOf("\/*");
		var cEndIdx = value.lastIndexOf("*\/");
		if(cStartIdx == -1 || cEndIdx == -1){
			throw new Error("JSON was not comment filtered");
		}
		return _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
	},
	javascript: function(xhr){ 
		// summary: A contentHandler which evaluates the response data, expecting it to be valid JavaScript

		// FIXME: try Moz and IE specific eval variants?
		return _d.eval(xhr.responseText);
	},
	xml: function(xhr){
		// summary: A contentHandler returning an XML Document parsed from the response data
		var result = xhr.responseXML;
		//>>excludeStart("webkitMobile", kwArgs.webkitMobile);
		if(_d.isIE &#038;&#038; (!result || !result.documentElement)){
			//WARNING: this branch used by the xml handling in dojo.io.iframe,
			//so be sure to test dojo.io.iframe if making changes below.
			var ms = function(n){ return "MSXML" + n + ".DOMDocument"; }
			var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)];
			_d.some(dp, function(p){
				try{
					var dom = new ActiveXObject(p);
					dom.async = false;
					dom.loadXML(xhr.responseText);
					result = dom;
				}catch(e){ return false; }
				return true;
			});
		}
		//>>excludeEnd("webkitMobile");
		return result; // DOMDocument
	},
	"json-comment-optional": function(xhr){
		// summary: A contentHandler which checks the presence of comment-filtered JSON and 
		//		alternates between the `json` and `json-comment-filtered` contentHandlers.
		if(xhr.responseText &#038;&#038; /^[^{\[]*\/\*/.test(xhr.responseText)){
			return handlers["json-comment-filtered"](xhr);
		}else{
			return handlers["json"](xhr);
		}
	}
};
</pre><p>What if we want to add our own content handler though?  All you need to do is add the key=&gt;parser to the <code>dojo.contentHandlers</code> object!</p><pre class="js">
// CSV parsing found at:  http://stackoverflow.com/questions/1293147/javascript-code-to-parse-csv-data

dojo.contentHandlers.csv = function(xhr) {
	
	// Set the data
	var responseText = xhr.responseText;
	var delimiter = ",";
	
	// Create a regular expression to parse the CSV values.
	var objPattern = new RegExp(
		 (
			  // Delimiters.
			  "(\\" + delimiter + "|\\r?\\n|\\r|^)" +

			  // Quoted fields.
			  "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +

			  // Standard fields.
			  "([^\"\\" + delimiter + "\\r\\n]*))"
		 ), "gi");


	// Create an array to hold our data. Give the array
	// a default empty first row.
	var arrData = [[]];

	// Create an array to hold our individual pattern
	// matching groups.
	var arrMatches = null;


	// Keep looping over the regular expression matches
	// until we can no longer find a match.
	while (arrMatches = objPattern.exec(responseText)){

		 // Get the delimiter that was found.
		 var strMatchedDelimiter = arrMatches[1];

		 // Check to see if the given delimiter has a length
		 // (is not the start of string) and if it matches
		 // field delimiter. If id does not, then we know
		 // that this delimiter is a row delimiter.
		 if (strMatchedDelimiter.length &#038;&#038; (strMatchedDelimiter != delimiter)){
			  // Since we have reached a new row of data,
			  // add an empty row to our data array.
			  arrData.push([]);
		 }
		
		 // Now that we have our delimiter out of the way,
		 // let's check to see which kind of value we
		 // captured (quoted or unquoted).
		 if (arrMatches[2]){

			  // We found a quoted value. When we capture
			  // this value, unescape any double quotes.
			  var strMatchedValue = arrMatches[2].replace(
				   new RegExp("\"\"", "g"),
				   "\""
				   );
		 } else {
			  // We found a non-quoted value.
			  var strMatchedValue = arrMatches[3];
		 }
		 // Now that we have our value string, let's add
		 // it to the data array.
		 arrData[arrData.length - 1].push(strMatchedValue);
	}

	// Return the parsed data.
	return(arrData);
}

</pre><p>The code snippet above allows you to have your XHR request&#8217;s result be parsed as CSV content;  the result becomes a JavaScript object representing the CSV data.  Here&#8217;s how you&#8217;d use it:</p><pre class="js">
dojo.xhrGet({
	url: "/ajax.php",
	handleAs: "csv",
	load: function(result) { // result is a JS object
		// Do something with the result here
	}
});
</pre><p>One key to flexibility within JavaScript framework is &#8220;dictionaries&#8221; or &#8220;property bags&#8221;, allowing for adding, removing, and modifying of existing properties.  Thanks to Dojo&#8217;s use of <code>dojo.contentHandlers</code> and <code>dojo.xhr</code>&#8216;s <code>handleAs</code> property, you can handle the result of your AJAX requests before they are passed to a callback!</p><p><a
href="http://davidwalsh.name/dojo-xhr-handleas">Custom AJAX Content Handling with the Dojo&nbsp;Toolkit</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/dojo-xhr-handleas/feed</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Drag and Drop MooTools File&#160;Uploads</title><link>http://davidwalsh.name/mootools-upload</link> <comments>http://davidwalsh.name/mootools-upload#comments</comments> <pubDate>Wed, 10 Aug 2011 14:05:24 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5279</guid> <description><![CDATA[Honesty hour confession:  file uploading within the web browser sucks.  It just does.  Like the ugly SELECT element, the file input is almost unstylable and looks different on different platforms.  Add to those criticism the fact that we&#8217;re all used to drag and drop operations, yet up until recently, you couldn&#8217;t drag files into a [...]<p><a
href="http://davidwalsh.name/mootools-upload">Drag and Drop MooTools File&nbsp;Uploads</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p>Honesty hour confession:  file uploading within the web browser sucks.  It just does.  Like the <a
href="http://davidwalsh.name/jquery-chosen">ugly SELECT element</a>, the file input is almost unstylable and looks different on different platforms.  Add to those criticism the fact that we&#8217;re all used to drag and drop operations, yet up until recently, you couldn&#8217;t drag files into a browser to upload them, making file uploading within the browser unintuitive.  With recent advancements in browser technology, the drag and drop method is now supported, but it doesn&#8217;t look good without a bit of work.  Luckily MooTools Core Developer Arian Stolwijk has created a <a
href="https://github.com/arian/mootools-form-upload" rel="nofollow">set of classes</a> to accommodate styling drag and drop file uploading within the browser.  Let&#8217;s have a look at how it works!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-upload.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>The&nbsp;HTML</h2><p>The basic setup is the same as a traditional form upload within the browser;  a <code>FORM</code> element with an <code>INPUT</code> of file type:</p><pre class="html">
&lt;form method="post" action="mootools-upload.php" enctype="multipart/form-data" id="uploadForm"&gt;
&lt;div&gt;
	&lt;div class="formRow"&gt;
		&lt;label for="file" class="floated"&gt;File: &lt;/label&gt;
		&lt;input type="file" id="file" name="file[]" multiple&gt;&lt;br&gt;
	&lt;/div&gt;

	&lt;div class="formRow"&gt;
		&lt;input type="submit" name="upload" value="Upload"&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;/form&gt;
</pre><p>This setup allows file uploading even if JavaScript is not enabled.  <em>(Note to IT snobs:  get over yourselves and turn JavaScript back on)</em></p><h2>The&nbsp;CSS</h2><p>The &#8220;drop zone&#8221; and &#8220;progress bar&#8221; areas can be easily styled in any fashion you&#8217;d like.  My sample CSS looks like this:</p><pre class="css">
.droppable {
	border: #ccc 1px solid;
	border-radius: 8px;
	background: #eee;
	color: #666;
	padding: 20px;
	margin: 10px;
	clear: both;
	text-align: center;
}

.droppable.hover {
	background: #ddd;
}

.uploadList {
	margin: 0;
	padding: 0;
	list-style: none;
}

.uploadItem {
	overflow: hidden;
	border-bottom: #BCBCBC 1px solid;
	margin: 0 20px;
	padding: 3px;
}

.uploadItem span {
	overflow: hidden;
	width: 150px;
	float: left;
	display: block;
}

a.addInputRow,
a.delInputRow,
.uploadItem a {
	display: inline-block;
	background: url(add.png) no-repeat;
	height: 16px;
	width: 16px;
	text-indent: -999px;
}

.uploadItem a {
	float: left;
	display: block;
	padding-left: 20px;
	background-image: url(delete.png);
}

a.delInputRow {
	background-image: url(delete.png);
}

.progress {
	margin: 5px 0;
	height: 15px;
	border-radius: 3px;
	background: #545A74;
}
</pre><p>Since we have (unfortunately) become accustomed to the ugly <code>INPUT type=file</code>, my only CSS advice is to make sure you make not only your &#8220;drop zone&#8221; apparent but explain that the user should drag and drop.</p><h2>The MooTools&nbsp;JavaScript</h2><p>Arian has provided 3 JavaScript classes within his mootools-form-upload repository:</p><ul><li><code>Form.Upload</code>:  The main worker class, detecting the browser capabilities and building a file uploader based on those features</li><li><code>Form.MultiFileInput</code>:  A class which builds and manages the list of files to be uploaded.</li><li><code>Request.File</code>:  Manages the FormData object, sends files, and reports progress.</li></ul><p>Another resource, iFrameFormRequest, can be included in case the user is rocking a legacy browser.  With the resources above added to the page, let&#8217;s set up our drag and drop file uploader:</p><pre class="js">
window.addEvent('domready', function(){
	
	// Create the file uploader
	var upload = new Form.Upload('file', {
		dropMsg: "Drop files here",
		onComplete: function(){
			alert('Files uploaded!');
		}
	});

	// Use iFrameFormRequest, which posts to iFrame 
	if (!upload.isModern()) {
		new iFrameFormRequest('uploadForm', {
			onComplete: function(response){
				alert('Files uploaded!');
			}
		});
	}

});
</pre><p>We start by creating an instance of <code>Form.Upload</code>, passing it the <code>INPUT</code> node and the class options.  The <code>onComplete</code> option is most important, as it represents the event that fires when all uploads have completed, allowing you to notify the user.</p><p>For more customizable uploads, like notifications for progress and success, you can pair <code>Form.MultipleFileInput</code> and <code>Request.File</code> directly:</p><pre class="js">
// From ReadMe.md

// the input element, the list (ul) and the drop zone element.
var input, list, drop;
// Form.MultipleFileInput instance
var inputFiles = new Form.MultipleFileInput(input, list, drop, {
    onDragenter: drop.addClass.pass('hover', drop),
    onDragleave: drop.removeClass.pass('hover', drop),
    onDrop: drop.removeClass.pass('hover', drop)
});

// Request instance;
var request = new Request.File({
    url: 'files.php'
    // onSuccess
    // onProgress
});

myForm.addEvent('submit', function(event){
    event.preventDefault();
    inputFiles.getFiles().each(function(file){
        request.append('url[]' , file);
    });
    request.send();
});

</pre><p>This solution would be good for using detailed progress bars.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-upload.php" class="demo">View Demo</a><div
class="clear"></div></div><p>Outstanding work once again by Arian.  His contribution to the MooTools JavaScript framework has been priceless, and he continues that effort with his drag and drop file upload system.  These classes prove the power of MooTools and the advancement of browsers today.  Give your users the elegant option of drag and drop uploads!</p><p><a
href="http://davidwalsh.name/mootools-upload">Drag and Drop MooTools File&nbsp;Uploads</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/mootools-upload/feed</wfw:commentRss> <slash:comments>16</slash:comments> </item> <item><title>Lazy Load Content and Widgets with MooTools and&#160;RequireJS</title><link>http://davidwalsh.name/lazy-load-requirejs</link> <comments>http://davidwalsh.name/lazy-load-requirejs#comments</comments> <pubDate>Tue, 12 Jul 2011 16:52:30 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[MooTools]]></category> <category><![CDATA[Optimization]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5266</guid> <description><![CDATA[The idea of lazy loading content is not new;  we&#8217;ve been lazy loading images (check out my MooTools LazyLoad plugin) for quite a while now.  And why wouldn&#8217;t you lazy load content?  There are SEO advantages, load time advantages, bandwidth savings, and more, so including a bit of extra JavaScript could help your large website immensely.  In [...]<p><a
href="http://davidwalsh.name/lazy-load-requirejs">Lazy Load Content and Widgets with MooTools and&nbsp;RequireJS</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p>The idea of lazy loading content is not new;  we&#8217;ve been lazy loading images (check out my MooTools LazyLoad plugin) for quite a while now.  And why wouldn&#8217;t you lazy load content?  There are SEO advantages, load time advantages, bandwidth savings, and more, so including a bit of extra JavaScript could help your large website immensely.  In my latest redesign, I took the time to lazy load not just images but static content and social networking widgets to make my website&#8217;s initial load happen faster to make user experience and Google rank improve.  Let me show you how I used MooTools and RequireJS to lazy load content.</p><p>So what am I lazy loading within my current design?</p><ul><li>Twitter, Facebook, and Google Plus widgets for single blog posts.</li><li>Page footer;  since the footer content is out of sight during initial page load, it doesn&#8217;t need to be loaded right away.</li><li>LighterJS Syntax Highlighter; if no PRE tag is on the page, there&#8217;s no reason to grab the syntax highlighter.</li><li>Comment avatar images;  there&#8217;s no point to loading these images immediately as they must be scrolled to.  If someone hits the &#8220;#comments&#8221; portion of the page, my LazyLoad plugin can handle loading the initial images.</li><li>Tweets;  tweet content is non-essential so they needn&#8217;t be loaded right away.</li><li>MooTools StarRating;  again, only needed if it&#8217;s a post page.</li></ul><p>Of course, there needs to be a trigger for these items to load.  I&#8217;ve chosen three events to trigger loading of these components:  keydown, mousemove, and scroll.  Each of these events signify a human and &#8220;interested&#8221; presence.  Let me show you how I&#8217;ve implemented this lazy loading.</p><h2>Load Initial&nbsp;Scripts</h2><p>Not every script can be asynchronously loaded, so the scripts I do load synchronously are RequireJS and Google&#8217;s JSAPI (for Analytics and Google Search):</p><pre class="js">
&lt;!-- JavaScript at the bottom for fast page loading --&gt; 
&lt;script src="http://ajax.cdnjs.com/ajax/libs/require.js/0.23.0/require.min.js"&gt;&lt;/script&gt; 
&lt;script src="https://www.google.com/jsapi?key=MY_KEY"&gt;&lt;/script&gt; 
&lt;script&gt; 
	// Load MooTools, Google Search
	function loadScripts() { 
		require(["http://davidwalsh.name/wp-content/themes/klass/js/script.js"]); 
	}
	google.load("mootools", "1.3.0");
	google.load("search", "1");
	google.setOnLoadCallback(loadScripts);
&lt;/script&gt;
</pre><p>Once the scripts are loaded, my theme&#8217;s main JavaScript resources is required.</p><h2>Lazy Loading&nbsp;Code</h2><p>Since there are multiple tasks I want to run when the user &#8220;awakens,&#8221; I create an onAwaken array that will hold each of the tasks.  A series of functions will be pushed to the array, each representing a sign task.  For example, the task which loads Twitter, Facebook, and Google +1 badges looks like:</p><pre class="js">
// Create the array of awaken functions
var onAwaken = [];

// Get the share URL from this page via the canonical link
var cannons = $$("link[rel=canonical]"), shareUrl;
if(cannons.length) {
	shareUrl = cannons[0].get("href");
}

// If the PROMO DIV exists on the page
var promoDiv = document.id("promo");
if(promoDiv &amp;&amp; shareUrl) {
	onAwaken.push(function(){
	
		// Create a SPAN and A for the Twitter share link
		var span1 = new Element("span",{ style:"display:inline-block;float:left;" }).inject(promoDiv);
		new Element("a",{
			href: "http://twitter.com/share",
			"class": "twitter-share-button",
			"data-count": "horizontal"
		}).inject(span1);
		
		// Inject the Twitter SCRIPT tag into the SPAN
		new Element("script",{
			src: "http://platform.twitter.com/widgets.js",
			async: true
		}).inject(span1);
		
		// Create the Facebook IFRAME
		new Element("iframe",{
			src: "http://www.facebook.com/plugins/like.php?href=" + shareUrl,
			scrolling: "no",
			frameborder: 0,
			allowTransparency: true,
			style: "border:none; overflow:hidden; width:500px;"
		}).inject(new Element("span",{style:"display:inline-block;"}).inject(promoDiv));
		
		// Create the G+1 Button
		var el = document.id(document.createElement("g:plusone")).set({
			href: shareUrl,
			size: "medium"
		}).inject(new Element("span",{style:"display:inline-block;"}).inject(promoDiv));
		
		// Get the G+ SCRIPT
		require(["http://apis.google.com/js/plusone.js"],function() {
			// console.warn("G+1 is ready!");
		});
		
		// Fade in all of the lazy loaded content
		promoDiv.fade(1);
	});
}
</pre><p>Note that a require call is within this function, which could probably be considering lazy loading during lazy loading.  In all seriousness though, it&#8217;s important to note that entire resources are lazy loaded, not just execution of available functionality.</p><p>Another onAwaken function includes loading of my footer and right column content:</p><pre class="js">
// Uses Moo to load and place content
function loadAndPlace(url,where,then) {
	new Request({
		url: url,
		onSuccess: function(content) {
			document.id(where).set("html",content);
			if(then) then();
		}
	}).send();
}

// Load footer and right column upon awakening
onAwaken.push(function() { 
	loadAndPlace(themePath + "footer.html","footer"); 
});
onAwaken.push(function() { 
	loadAndPlace(themePath + "follow-trends.html","contentRightColumn", function(){ 
		$$(".contentRightColumn").fade(1); 
	}); 
});
</pre><p>And then I also lazy load the MooTools StarRating plugin:</p><pre class="js">
// Star ratings
var ratingForm = document.id("ratingForm"),
	themePath = "/wp-content/themes/klass/";
if(ratingForm) {
	// Get the post id
	var postId = document.id("postId").get("value");
	
	// Get star ratings
	require([themePath + "js/ratings/Source/moostarrating.js"], function() {
		// Set image vars
		MooStarRatingImages = {
			defaultImageFolder: themePath + 'js/ratings/Graphics/',
			defaultImageEmpty:  'star_boxed_empty.png',
			defaultImageFull:   'star_boxed_full.png',
			defaultImageHover:  'star_boxed_hover.png'
		};
		// Create star ratings
		var voted = false;
		var rater = new MooStarRating({
			form: ratingForm,
			radios: "rating",
			half: true,
			width: 16
		}).addEvent("click",function(value) {
			if(!voted){
				// Send request
				new Request({
					url: themePath + "rating.php",
					method: "post",
					data: {
						rating: value,
						id: postId
					}
				}).send();
				// Mark as voted
				voted = true;
			}
		});
		// Fix the value...wtf?!
		// Adding 0.5 because it's always showing up short 0.5 short.  WTF.
		rater.setValue((rater.currentIndex * 0.5) + 0.5);
		// Showtime
		ratingForm.setStyle("display","block");
	});
}
</pre><p>There is no limit to the number of functions you can fire when the user awakens, so let&#8217;s assume I have a bunch more functions ready to run.  Now it&#8217;s time to place JavaScript code into place to listen for the page&#8217;s awakening.  Here it goes:</p><pre class="js">
// Set up main register handler
var runEvents = ["keydown","mousemove","scroll"];
var runAwaken = function() {
	onAwaken.each(function(evt,index) {
		evt();
	});
	// Since we've run all items, let's remove the events
	runEvents.each(function(evtType){
		window.removeEvent(evtType,runAwaken);
	});
};
// Set registers in motion
runEvents.each(function(evtType){
	window.addEvent(evtType,runAwaken);
});
</pre><p>First we create an array of events we want the page to awaken on.  Next we create a function which will run upon awakening;  this function will run all awaken events and then remove the event from the window so that the runAwaken function will only run once.</p><p>The sky is the limit with the content you can lazy load.  I highly recommend evaluating your website for components which don&#8217;t hold search engine value, or may speed up the load of your page (especially synchronous SCRIPT tags!).  As always, have fun with your websites and do what you can to make them load faster!</p><p><a
href="http://davidwalsh.name/lazy-load-requirejs">Lazy Load Content and Widgets with MooTools and&nbsp;RequireJS</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/lazy-load-requirejs/feed</wfw:commentRss> <slash:comments>11</slash:comments> </item> <item><title>MooTools History&#160;Plugin</title><link>http://davidwalsh.name/mootools-history</link> <comments>http://davidwalsh.name/mootools-history#comments</comments> <pubDate>Mon, 02 May 2011 14:18:45 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[MooTools]]></category> <category><![CDATA[Optimization]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5209</guid> <description><![CDATA[One of the reasons I love AJAX technology so much is because it allows us to avoid unnecessary page loads.  Why download the header, footer, and other static data multiple times if that specific data never changes?  It&#8217;s a waste of time, processing, and bandwidth.  Unfortunately, at this point in the web, constant refreshes are [...]<p><a
href="http://davidwalsh.name/mootools-history">MooTools History&nbsp;Plugin</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p>One of the reasons I love AJAX technology so much is because it allows us to avoid unnecessary page loads.  Why download the header, footer, and other static data multiple times if that specific data never changes?  It&#8217;s a waste of time, processing, and bandwidth.  Unfortunately, at this point in the web, constant refreshes are the norm &#8212; but they don&#8217;t have to be.  Christoph Pojer, a MooTools Core Developer, has added History to his <del>PojerTools</del> PowerTools library.  History replaces traditional same-site URL loading by providing a method to catch link clicks, load page content via AJAX (Mootools&#8217; <code>Request.HTML</code> class), modify the document&#8217;s location object to keep &#8220;history&#8221; records, and re-evaluate content links to allow developers to create a fast, efficient one-page website.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-history.php" class="demo">View Demo</a><div
class="clear"></div></div><p>The tradition method of dynamic history/&#8221;back button&#8221; management has always been hash-based JavaScript technology.  Newer technology, including <a
href="https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history" rel="nofollow">HTML5&#8242;s <code>window.onpopstate</code> and <code>history.pushState</code></a> methods, allow for more reliable methods for managing history.  MooTools&#8217; History plugin supports modern and legacy methods for history management.  Let me show you how to quickly implement the MooTools History plugin.</p><h2>HTML&nbsp;Structure</h2><p>The History plugin doesn&#8217;t require any HTML structure adjustments but at least one designated content should be identified; you can, of course, have as many content areas as you like, but you&#8217;ll most likely need multiple AJAX requests to retrieve their content, unless you use a <code>Request.JSON</code> request to retrieve content for multiple areas of the page.  For this simple demo, we&#8217;ll define a header, footer, and menu, and content area:</p><pre class="html">
&lt;div id="body"&gt;
	
	&lt;!-- header --&gt;
	&lt;header&gt;
		&lt;a href="/" data-noxhr&gt;David Walsh Blog&lt;/a&gt; 
		&lt;div&gt;MooTools History Plugin Demo&lt;/div&gt;
		&lt;div&gt;This is a simple example of the MooTools History plugin created by Christoph Pojer&lt;/div&gt;
		
	&lt;/header&gt;
		
	&lt;!-- menu --&gt;
	&lt;ul id="demoMenu"&gt;
		&lt;li&gt;&lt;a href="mootools-history.php"&gt;Home&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="mootools-history-david.php"&gt;About David Walsh&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="mootools-history-mootools.php"&gt;About MooTools&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="mootools-history-christoph"&gt;About Christoph Pojer&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
	
	&lt;!-- content area --&gt;
	&lt;article id="contentHolder"&gt;
		
		&lt;!-- initial page content goes here --&gt;
		
	&lt;/article&gt;
	
	
	&lt;!-- footer --&gt;
	&lt;footer&gt;
	
	&lt;/footer&gt;
&lt;/div&gt;
</pre><p>The content area is the only area which will have its content change.  The page should load as usual</p><h2>The MooTools&nbsp;JavaScript</h2><p>Assuming that the <a
href="https://github.com/cpojer/mootools-history" rel="nofollow">MooTools History plugin</a> has been included in the page, there are a few functions that should be created upon <code>domready</code>.  The first is a method which will perform the request for content when a link is clicked:</p><pre class="js">
// Content holder (all content placed within this element)
var contentHolder = document.id("contentHolder");

// Create a Request object which will be reused when links are clicked
var request = new Request.HTML({
	onSuccess: function(nodeTree,elements,html) {
		// Set the content into the content holder
		contentHolder.set("html",html);
		// Execute directions that should be executed whenever a page changes
		onPageUpdate();
	}
});

// Create a function that loads the page content
var loadPage = function(url) {
	// Make a HTML request to get the content for this page
	request.send({ url: url });
};
</pre><p>The next step is creating a method <em>(which is theoretically option, but you&#8217;ll usually want to do something once content has loaded)</em> which will execute every time content is received:</p><pre class="js">
// Function that will execute whenever a page gets changed
var onPageUpdate = function() {
	
	// Do whatever you'd like here!  
	
	// Possibly manually record a Google Analytics page view?
	
};
</pre><p>History doesn&#8217;t request that you do anything when content is received, but you&#8217;ll likely want to do something.  Why manually <a
href="http://davidwalsh.name/ajax-analytics">record a page view within Google Analytics</a>?</p><p>This next piece is important in turning links to static pages into AJAX-ified History triggers.  Just one big <a
href="http://mootools.net/docs/more/Element/Element.Delegation" rel="nofollow">Element.Delegation</a> event delegation call will do the work for not just the initial page load, but every History AJAX load after that:</p><pre class="js">
// The listener that manages all clicks
var listener = function(evt){
	evt.preventDefault(); // Prevent following the URL
	History.push(this.get('href')); // Push the new URL into History
};

// Add event delegation to add clicks.  Both of these work:
//document.body.addEvent("click:relay(a:not([href=#]):not([href^=http://]):not([data-noxhr]))",listener);
document.body.addEvent("click:relay(a:not([href=#],[href^=http://],[data-noxhr]))",listener);
</pre><p>When any same-site, non-hashed link is clicked, the listener method stops the event and pushes the new URL into History, changing the address bar and managing back/forward button click.</p><p>A <code>back</code> function is also created so that we can provide a &#8220;back&#8221; link and a &#8220;forward&#8221; link to travel back and forward in page history, if we choose to use it:</p><pre class="js">
// Listener for the "Back" link
var back = function(evt){
	evt.preventDefault();
	History.back(); // Go back
};

// Listener for the "Forward" link
var forward = function(evt){
	evt.preventDefault();
	History.forward(); // Go back
};

// Add to links
document.id("backLink").addEvent("click",back);
document.id("forwardLink").addEvent("click",forward);
</pre><p>The next step is adding a <code>change</code> event to History itself to run our <code>loadPage</code> function when the page URL changes:</p><pre class="js">
// When the history changes, update the content 
History.addEvent('change',loadPage);
</pre><p>If the client doesn&#8217;t support the <code>history.pushState</code> method, the History plugin evaluates the hash and loads the page as necessary:</p><pre class="js">
// Handle the initial load of the page if the browser does not support pushState, check if the hash is set
if(!History.hasPushState()) {
	// Check if there is a hash
	var hash = document.location.hash.substr(1);
	if (!hash) return;

	// If the hash equals the current page, don't do anything
	var path = document.location.pathname.split('/');
	path = path[path.length - 1];
	if (hash == path) return;

	// Load the page specified in the hash
	loadPage(hash);
}
</pre><p>Lastly, running the <code>onPageUpdate</code> upon <code>domready</code> load doesn&#8217;t hurt since events are only added once within <code>onPageUpdate</code>:</p><pre class="js">
// Update the page
onPageUpdate();
</pre><p>Now the page is ready to support History-based, AJAX-driven content swapping.  Thanks to the <code>onPageUpdate</code> function, links are added to events as they come in so that even AJAX-retrieved content can be managed with History.</p><h2>Tips and Strategies for Hash/History-Managed&nbsp;Websites</h2><p>Plugins like Christoph&#8217;s History masterpiece are very helpful in enriching the user experience but do require a bit of developer logic:</p><ul><li><strong>Use Event Delegation</strong> &#8211; Remember that with a History-style system, assigning events to elements directly may not be the best solution because those elements may be gone with the next link click.  Using event delegation instead of traditional event assignments may save you a lot of trouble. Read my <a
href="http://davidwalsh.name/event-delegation">MooTools Element.Delegation</a> post if you aren&#8217;t familiar with event delegation.</li><li><strong>Don&#8217;t Assume JavaScript Support</strong> &#8211; Keep in mind that the client may not support JavaScript.  Search engines have added JavaScript support but it&#8217;s important to use URLs that will work with both on a History-managed site and a JavaScript-less website.</li><li><strong>Use AJAX Detection </strong>- MooTools provides an AJAX-specific header within the Request class called <code>HTTP_X_REQUESTED_WITH</code>.  <a
href="http://davidwalsh.name/detect-ajax">Click here</a> to learn how to use it to detect AJAX requests.  You will want to be able to detect AJAX so that those requests simply return the content and not the header and footer (etc.) with it. You could write a client-side script/regex to parse out the content but that&#8217;s largely inefficient.  My demo uses PHP to store the page content in variables as follows: <br
/><br
/><pre class="php">
// Load pages based on querystring
$qstring = $_SERVER['QUERY_STRING'];
if($qstring == 'home' || $qstring == '') {
	$content.= '&lt;h1&gt;Welcome Home!&lt;/h1&gt;';
	$content.= '&lt;p&gt;History Management via popstate or hashchange. Replaces the URL of the page without a reload and falls back to Hashchange on older browsers.&lt;/p&gt;&lt;p&gt;This demo page aims to teach you how you can use Christoph Pojer\'s outstanding History widget to load only the content you need, dynamically and reliably.&lt;/p&gt;';
}
elseif($qstring == 'about-david') {
	$content.= '&lt;h1&gt;About David Walsh&lt;/h1&gt;';
	$content.= '&lt;p&gt;My name is David Walsh. I\'m a 27 year old Web Developer from Madison, Wisconsin. In the web world, I am:&lt;/p&gt;
	&lt;ul&gt;
	&lt;li&gt;Founder and Lead Developer for Wynq Web Labs.&lt;/li&gt;
	&lt;li&gt;Software Engineer for SitePen.&lt;/li&gt;
	&lt;li&gt;Core Developer for the MooTools JavaScript framework.&lt;/li&gt;
	&lt;li&gt;Co-Founder of Script &amp; Style, a website aimed at making web developers and designers better.&lt;/li&gt;
	&lt;/ul&gt;
	&lt;p&gt;I don\'t design the websites - I make them work.&lt;/p&gt;
	&lt;p&gt;I am also an admirer of the great &lt;a href="?about-christoph"&gt;Christoph Pojer!&lt;/a&gt;.&lt;/p&gt;';
}
// and more....
// Page not found
else {
	$content.= '&lt;h1&gt;Page Not Found&lt;/h1&gt;';
	$content.= '&lt;p&gt;The page you were attempting to find could not be found.&lt;/p&gt;';
}

// If request was via AJAX, push it out.
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) &amp;&amp; strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
	echo $content;
	exit();
}
</pre>Obviously your content management system would be pulling content from a database or other static files, but you get the point &#8212; load content before any page output, sniff for AJAX, and push content out accordingly.  If it&#8217;s not an AJAX request, push that content into the content area&#8217;s HTML via traditional methods.</li></ul><p>These tips should set you up well to use a History-based system.  Remember that JavaScript is meant to enhance &#8212; keep in mind that your user (or search engine bot) may not support JavaScript, so be sure to test your website thoroughly!</p><p><em>Give the example hell.  Click from page to page, use the back button, refresh the page, etc.  History is rock solid!</em></p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-history.php" class="demo">View Demo</a><div
class="clear"></div></div><p>Thanks to Christoph Pojer for his outstanding MooTools History plugin.  Many History-style plugins have existed but the browsers haven&#8217;t been as feature-rich as they are now.  If you have any suggestions, tips, or experiences to share about creating hash-based websites, please share them.</p><p><a
href="http://davidwalsh.name/mootools-history">MooTools History&nbsp;Plugin</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/mootools-history/feed</wfw:commentRss> <slash:comments>19</slash:comments> </item> <item><title>RealTime Stock Quotes with MooTools Request.Stocks and&#160;YQL</title><link>http://davidwalsh.name/javascript-stock-quotes</link> <comments>http://davidwalsh.name/javascript-stock-quotes#comments</comments> <pubDate>Wed, 23 Feb 2011 14:18:23 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[APIs]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[Markup]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5174</guid> <description><![CDATA[It goes without saying but MooTools&#8217; inheritance pattern allows for creation of small, simple classes that possess immense power.  One example of that power is a class that inherits from Request, Request.JSON, and Request.JSONP:  Request.Stocks.  Created by Enrique Erne, this great MooTools class acts as a wrapper for Request.JSON and Yahoo!&#8217;s YQL service.  Let me [...]<p><a
href="http://davidwalsh.name/javascript-stock-quotes">RealTime Stock Quotes with MooTools Request.Stocks and&nbsp;YQL</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p>It goes without saying but MooTools&#8217; inheritance pattern allows for creation of small, simple classes that possess immense power.  One example of that power is a class that inherits from Request, Request.JSON, and Request.JSONP:  <a
href="http://mootools.net/forge/p/request_stocks">Request.Stocks</a>.  Created by Enrique Erne, this great MooTools class acts as a wrapper for Request.JSON and Yahoo!&#8217;s YQL service.  Let me show you how to user Enrique&#8217;s awesome MooTools JavaScript class!</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/request-stocks.php" class="demo">View Demo</a> <a
href="http://mootools.net/forge/p/request_stocks" class="demo">Download Request.Stocks</a><div
class="clear"></div></div><h2>The&nbsp;HTML</h2><p>The stock content will be built by javascript but we will have two DIVS created within the page:</p><pre class="html">
&lt;div id="stockIndicator"&gt;Retrieving stock information....&lt;/div&gt;
&lt;div id="stockContainer"&gt;&lt;/div&gt;
</pre><p>One DIV is an indicator which fades in an out during data fetch, the other is the container which will hold the stock information.</p><h2>The&nbsp;CSS</h2><p>As always, the styling of elements is subject to however you&#8217;d like them to look, but here&#8217;s a really basic style set:</p><pre class="css">
#stockContainer, #stockIndicator { width:500px; }
#stockContainer		{ background:#343434; padding:10px; }
#stockIndicator		{ text-align:right; visibility:hidden; padding:5px; }
.stockRow			{ padding:5px 10px; clear:both; margin-bottom:5px; }
	.stockName		{ font-size:14px; font-weight:normal; float:left; }
	.stockData		{ font-size:11px; float:right; width:200px; text-align:right; }

/* stock differential */`
.stockSame			{  }
.stockUp			{ background:lightgreen; }
	.stockUp .stockName	{ color:green; }
.stockDown			{ background:pink; }
	.stockDown .stockName { color:red; }
</pre><p>Stocks that are above for the day are green, below are red.  Stocks which have stayed the same are black.  Style however you&#8217;d like though.</p><h2>The MooTools&nbsp;JavaScript</h2><p>Before attacking an example usage, let&#8217;s talk about about Request.Stocks.  This class is an extension of JSONP, setting numerous defaults that correspond to using Yahoo&#8217;s YQL service, and adding additional options to customize the data you want to receive from Yahoo.  A few of the custom options include:</p><ul><li>stocks: An array of symbols to retrieve stocks for</li><li>sortBy: The data field to sort by</li><li>display: The data fields to retrieve from Yahoo.  A few of the fields you can retrieve include:  Ask, AverageDailyVolume, Bid, AskRealtime, BidRealtime, BookValue, Change&amp;PercentChange, Change, Commission, ChangeRealtime, AfterHoursChangeRealtime, DividendShare, LastTradeDate, TradeDate, and more!</li></ul><p>More detail on all of the custom options, some of which are not listed on this blog, <a
href="http://2011.mild.ch/projects/request-stocks.html">can be found here</a>.</p><p>So now to the class usage.  The sample code looks long but that&#8217;s due to my commenting:</p><pre class="js">
// When the dom is ready...
window.addEvent("domready",function() {
	// Get the stock container and indicator
	var container = document.id("stockContainer");
	var indicator = document.id("stockIndicator");
	// Create the instance
	var request = new Request.Stocks({
		// Stocks to retrieve
		stocks: ["AAPL","GOOG","MSFT"],
		// Formatter upon result
		onComplete: function(result) {
			// Log out the result to see the structure
			console.warn(result);
			// Hide the indicator
			indicator.fade(0);
			// Create a template with which to display the information
			var template = ' \
				&lt;div class="stockRow stock{UpDown}"&gt; \
					&lt;div class="stockName"&gt;({symbol}) {Name}&lt;/div&gt;\
					&lt;div class="stockData"&gt;{Ask} {Change} {ChangeinPercent}&lt;/div&gt; \
					&lt;br class="clear" /&gt; \
				&lt;/div&gt; \
			';
			// For every stock , display its information in the template
			var html ="";
			Array.from(result.query.results.quote).each(function(quote) {
				// Add an "UpDown" property to the quote object
				var change = Number.from(quote.Change);
				quote.UpDown =  change == 0 ? "Same" : change &gt; 0 ? "Up" : "Down";
				// Update running contnet
				html += template.substitute(quote);
			});
			// Update the content
			container.set("html",html);
		},
		// Show the indicator upon request
		onRequest: function () {
			indicator.fade(1);
		}
	});
	// Send the request every 20 seconds
	(function() {
		request.send();
	}).periodical(20 * 1000);
	// Send right away
	request.send();
</pre><p>Request.Stocks&#8217; onComplete method provides a method to handle the result from Yahoo.  Notice how I build the content string (using MooTools&#8217; string.substitute) and then set the element&#8217;s HTML, instead of appending to the element&#8217;s innerHTML property &#8212; that&#8217;s the more efficient way to change a node&#8217;s content.  Also, remember that this class contains every method that Request, Request.JSON, and Request.JSONP have.</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/request-stocks.php" class="demo">View Demo</a> <a
href="http://mootools.net/forge/p/request_stocks" class="demo">Download Request.Stocks</a><div
class="clear"></div></div><p>Request.Stocks is an outstanding utility class for developers looking to create applications based on stock market data.  The mix of flexibility and simplicity are real strong points of this class.  If I had one criticism of this class, it would be that it doesn&#8217;t provide a method to add or remove stocks on the fly.  This functionality would allow the developer to create applications which allow users to add their own portfolio stocks without needing them all defined immediately.  Other than that, this class rock solid and I look forward to seeing where it goes in the future!</p><p><a
href="http://davidwalsh.name/javascript-stock-quotes">RealTime Stock Quotes with MooTools Request.Stocks and&nbsp;YQL</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/javascript-stock-quotes/feed</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>MooTools Star Ratings with&#160;MooStarRating</title><link>http://davidwalsh.name/mootools-star-rating</link> <comments>http://davidwalsh.name/mootools-star-rating#comments</comments> <pubDate>Tue, 08 Feb 2011 14:59:12 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[MooTools]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[PHP]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5166</guid> <description><![CDATA[I&#8217;ve said it over and over but I&#8217;ll say it again:  JavaScript&#8217;s main role in web applications is to enhance otherwise boring, static functionality provided by the browser.  One perfect example of this is the Javascript/AJAX-powered star rating systems that have become popular over the past five years.  Star rating systems are attractive, allow us [...]<p><a
href="http://davidwalsh.name/mootools-star-rating">MooTools Star Ratings with&nbsp;MooStarRating</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<a
href="http://davidwalsh.name/dw-content/mootools-star-rating.php"><img
src="http://davidwalsh.name/dw-content/starRating.png" alt="MooTools Star Rating" class="image" /></a><p>I&#8217;ve said it over and over but I&#8217;ll say it again:  JavaScript&#8217;s main role in web applications is to enhance otherwise boring, static functionality provided by the browser.  One perfect example of this is the Javascript/AJAX-powered star rating systems that have become popular over the past five years.  Star rating systems are attractive, allow us to avoid ugly forms, and prevent unnecessary page reloads.  A new plugin by Lorenzo Stanco called <a
href="http://mootools.net/forge/p/moostarrating" rel="nofollow">MooStarRating</a> has hit the MooTools Forge and I wanted to share with you how to use it.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-star-rating.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>The&nbsp;HTML</h2><p>The star rating system uses an HTML form with radio buttons as the base:</p><pre class="html">
&lt;form name="ratingsForm"&gt;
    &lt;label&gt;Do you like this post?&lt;/label&gt;
    &lt;input type="radio" name="rating" value="0.5"&gt;
    &lt;input type="radio" name="rating" value="1.0"&gt;
    &lt;input type="radio" name="rating" value="1.5"&gt;
    &lt;input type="radio" name="rating" value="2.0"&gt;
    &lt;input type="radio" name="rating" value="2.5"&gt;
    &lt;input type="radio" name="rating" value="3.0"&gt;
    &lt;input type="radio" name="rating" value="3.5"&gt;
    &lt;input type="radio" name="rating" value="4.0"&gt;
    &lt;input type="radio" name="rating" value="4.5"&gt;
    &lt;input type="radio" name="rating" value="5.0"&gt;
    &lt;input type="radio" name="rating" value="5.5"&gt;
    &lt;input type="radio" name="rating" value="6.0"&gt;
    &lt;input type="radio" name="rating" value="6.5"&gt;
    &lt;input type="radio" name="rating" value="7.0" checked="checked"&gt;
    &lt;input type="radio" name="rating" value="7.5"&gt;
    &lt;input type="radio" name="rating" value="8.0"&gt;
    &lt;input type="radio" name="rating" value="8.5"&gt;
    &lt;input type="radio" name="rating" value="9.0"&gt;
    &lt;input type="radio" name="rating" value="9.5"&gt;
    &lt;input type="radio" name="rating" value="10.0"&gt;
	&lt;span id="htmlTip"&gt;&lt;/span&gt;
&lt;/form&gt;
</pre><p>Note the ID of the form and the name of the radio buttons &#8212; we&#8217;ll use those when creating our MooStarRating instance.  Also note that I&#8217;m creating &#8220;half&#8221; rating options, as well as using checked to note what the current average rating is.</p><h2>The&nbsp;CSS</h2><p>This plugin requires no additional CSS.  That&#8217;s a bonus as it&#8217;s one less server request.</p><h2>The MooTools&nbsp;JavaScript</h2><p>The first step in using MooStarRating is defining the image paths for the stars:</p><pre class="js">
// Configure the image paths
var MooStarRatingImages = {
	defaultImageFolder: "/js/mooStarRating/images",
	defaultImageEmpty:  "empty.png",
	defaultImageFull:   "full.png",
	defaultImageHover:  "hover.png"
};
</pre><p>Once the path and image names are defined, it&#8217;s time to create an instance of MooStarRating:</p><pre class="js">
// A fake post ID for the sake of submission
var postId = 10;

// When the DOM is ready....
window.addEvent("domready",function() {	
	// Create our instance
	var starRater = new MooStarRating({
		form: "ratingsForm",
		radios: "rating",
		half: true,
		//imageEmpty: "star_boxed_empty.png", // For setting special images
		//imageFull:  "star_boxed_full.png",
		//imageHover: "star_boxed_hover.png", 
		width: 17, 
		tip: "Rate as &lt;i&gt;[VALUE] / 10.0&lt;/i&gt;", 
		tipTarget: document.byId("htmlTip"),
		tipTargetType: "html"
	});
	
	// Listen for star clicks
	starRater.addEvent("click",function(value) {
		// Send ajax request to server
		new Request.send({
			url: "rating.php",
			data: { rating: value, postId: postId }
		});
	});
});

</pre><p>MooStarRating is loaded with options.  Here we pass the form ID and the name we provided to the radio buttons.  As I&#8217;m allowing half-stars, the <code>half</code> option is set to true.  MooStarRating also provides a &#8220;tip&#8221; functionality which allows a message to be displayed along side the star rating.  Lastly, the <code>click</code> event provides the user&#8217;s rating for which you may send an AJAX request to the server to save the rating.  Simple!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/mootools-star-rating.php" class="demo">View Demo</a><div
class="clear"></div></div><p>That&#8217;s it!  I love this plugin because it&#8217;s simple and effective.  Big props go to Lorenzo Stanco for his excellent piece of work.  If there&#8217;s enough interest, I&#8217;ll create a tutorial that includes enough PHP and MySQL to get this rating system working with real data.</p><p><a
href="http://davidwalsh.name/mootools-star-rating">MooTools Star Ratings with&nbsp;MooStarRating</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/mootools-star-rating/feed</wfw:commentRss> <slash:comments>32</slash:comments> </item> <item><title>LightFace:  Facebook Lightbox for&#160;MooTools</title><link>http://davidwalsh.name/facebook-lightbox</link> <comments>http://davidwalsh.name/facebook-lightbox#comments</comments> <pubDate>Tue, 09 Nov 2010 04:29:23 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[Markup]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5108</guid> <description><![CDATA[One of the web components I&#8217;ve always loved has been Facebook&#8217;s modal dialog.  This &#8220;lightbox&#8221; isn&#8217;t like others:  no dark overlay, no obnoxious animating to size, and it doesn&#8217;t try to do &#8220;too much.&#8221;  With Facebook&#8217;s dialog in mind, I&#8217;ve created LightFace:  a Facebook lightbox clone for MooTools.  LightFace and its family of classes work [...]<p><a
href="http://davidwalsh.name/facebook-lightbox">LightFace:  Facebook Lightbox for&nbsp;MooTools</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p><a
href="http://davidwalsh.name/dw-content/lightface.php"><img
src="http://davidwalsh.name/dw-content/lightface.png" alt="Facebook Lightbox MooTools" /></a></p><p>One of the web components I&#8217;ve always loved has been Facebook&#8217;s modal dialog.  This &#8220;lightbox&#8221; isn&#8217;t like others:  no dark overlay, no obnoxious animating to size, and it doesn&#8217;t try to do &#8220;too much.&#8221;  With Facebook&#8217;s dialog in mind, I&#8217;ve created LightFace:  a Facebook lightbox clone for MooTools.  LightFace and its family of classes work well with iFrames, images, AJAX-requested content, static positioning, and static content.</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/lightface.php" class="demo">View Demos</a> <a
href="http://mootools.net/forge/p/lightface" class="demo">Download LightFace</a> <a
href="http://davidwalsh.name/js/lightface" class="demo">Updated Documentation</a><div
class="clear"></div></div><h2>LightFace&nbsp;Features</h2><p>LightFace has a lot of backed in goodness!</p><ul><li>Five compact classes: <code>LightFace</code>, <code>LightFace.Request</code>, <code>LightFace.IFrame</code>, <code>LightFace.Image</code>, and <code>LightFace.Static</code></li><li>Constrains image sizes with window resizes</li><li>Provides a host of options to customize each instance</li><li>Responds to designated keyboard events</li><li>Works with MooTools More&#8217;s <code><a
href="http://mootools.net/more/">Drag</a></code> class to make the lightbox movable <em>(Drag is *not* included within the repo)</em></li><li>Add as many buttons as you&#8217;d like</li><li>Adds and removes events as needed to minimize strain on the browser</li><li>Automatically positions and resizes with window resizing and scrolling</li><li>Supports IE6+, Safari, Chrome, Opera, and iPad/iPhone</li></ul><h2>LightFace&nbsp;Core</h2><p>LightFace.js is the core piece of LightFace.  All subsequent classes extend the core functionality provided by LightFace.  Creating a new LightFace lightbox is as easy as:</p><pre class="js">
// Create instance
var modal = new LightFace({
	height: 200,
	width: 300,
	title: 'My Profile,
	content: 'Lorem ipsum....'
});

// Open Sesame!
modal.open();

//Update Content
modal.load('This is different content....');
</pre><p>LightFace provides a wealth of flexibility by providing numerous options to customize the lightbox as you&#8217;d like:</p><ul><li><strong>width</strong> &#8211; <em>(*integer|string*, defaults to &#8216;auto&#8217;)</em> The desired width of the of the modal box.</li><li><strong>height</strong> &#8211; <em>(*string|string*, defaults to &#8216;auto&#8217;)</em> The desired height of the of the modal box.</li><li><strong>draggable</strong> &#8211; <em>(*boolean*, defaults to false)</em> Should the modal box be draggable by its title?</li><li><strong>title</strong> &#8211; <em>(*string*, defaults to &#8221;)</em> The modal&#8217;s initial title.</li><li><strong>content</strong> &#8211; <em>(*string*, defaults to &#8216;&lt;p&gt;Message not specified.&lt;/p&gt;&#8217;)</em> The modal&#8217;s initial content.</li><li><strong>buttons</strong> &#8211; <em>(*array*, defaults to [])</em> An array containing any number of objects containing button information.</li><li><strong>fadeDelay</strong> &#8211; <em>(*integer*, defaults to 150)</em> The delay before instructing the overlay to fade in/out.</li><li><strong>fadeDuration</strong> &#8211; <em>(*integer*, defaults to 150)</em> The duration of overlay fade while content is loading.</li><li><strong>keys</strong> &#8211; <em>(*object*, defaults to object w/ &#8216;esc&#8217; key handler)</em> Key handlers to add events to while the modal box is open.</li><li><strong>zIndex</strong> &#8211; <em>(*integer*, defaults to 9001)</em> The desired zIndex of the modal.</li><li><strong>constrain</strong> &#8211; <em>(*boolean*, defaults to false)</em> Should the modal box constrain content when the window is resized?</li><li><strong>errorMessage</strong> &#8211; <em>(*string*, defaults to &#8216;<p>The requested file could not be found.</p>&#8216;)</em> The error message displayed if a resource is not found.</li><li><strong>resetOnScroll</strong> &#8211; <em>(*boolean*, defaults to true)</em> Keeps the modal box in the same place on the screen if the user scrolls.</li></ul><p>LightFace features many methods to let you control the content and flow of each LightFace instance:</p><ul><li><strong>load(content,title?)</strong> &#8211; loads specified content into the lightbox</li><li><strong>open(fast?)</strong> &#8211; opens the lightbox</li><li><strong>close</strong> &#8211; closes the lightbox</li><li><strong>fade</strong> &#8211; fades in the &#8220;loading&#8221; overlay</li><li><strong>unfade</strong> &#8211; fades out the &#8220;loading&#8221; overlay</li><li><strong>getBox</strong> &#8211; returns the entire DOM node so you may update the node itself at will</li><li><strong>addButton</strong> &#8211; adds a button to the lightbox footer</li><li><strong>showButton</strong> &#8211; shows a button</li><li><strong>hideButton</strong> &#8211; hides a button</li></ul><p><a
href="http://davidwalsh.name/dw-content/lightface.php"><img
src="http://davidwalsh.name/dw-content/lightface3.png" alt="Facebook Dialog MooTools" /></a></p><h2>LightFace.Request</h2><p>LightFace.Request merges the powers of LightFace and MooTools&#8217; Request (AJAX) class to load content into the lightbox when desired.  LightFace features an internal overlay and Facebook-style indicator which elegantly fades in and out during the time the AJAX request is running.  LightFace adds two additional options:  <code>url</code> and <code>request</code>.  The <code>request</code> option represents the object to be passed directly to LightFace&#8217;s internal Request class instance.  Here&#8217;s what a usage of LightFace.Request would look like:</p><pre class="js">
// Create the instance
var modal = new LightFace.Request({
	width: 400,
	height: 300,
	title: 'User Information',
	url: 'user.php',
	request: {
		method: 'post',
		data: {
			userID: 3
		}
	}
});

// Open!
modal.open();

// Load a different url!
modal.load('content.php','Static Content');
</pre><p>An AJAX request is made to the url provided.  LightFace.Request mixes the settings provided with the internal Request class&#8217; default settings so you always have callbacks once the request is complete!</p><p><a
href="http://davidwalsh.name/dw-content/lightface.php"><img
src="http://davidwalsh.name/dw-content/lightface2.png" alt="Facebook Modal MooTools" /></a></p><h2>LightFace.Image</h2><p>LightFace.Image specializes in loading images within the lightbox.  The advantage to using LightFace.Image is that the lightbox will constrain the images to appropriate height and width with relation to the window size.  If the user resizes their browser, the image will resize appropriately.</p><pre class="js">
var light = new LightFace.Image({
	title: 'Image ' + (index + 1),
	fadeDuration: 100,
	fadeDelay: 400,
	keys: {
		left: function() { //load the previous image
			if(index!= 0) this.load(images[--index],'Image ' + (index + 1));
		},
		right: function() { //load the next image
			if(index != images.length-1) this.load(images[++index],'Image ' + (index + 1));
		},
		esc: function() {
			this.close();
		}
	}
});
</pre><p>If you want certain images to load in an IFrame, with the following HTML format:</p><pre class="html">
&lt;a href="large.jpg" rel="lightface"&gt;&lt;img src="thumb.jpg" alt="My Image" title="Click for larger view" /&gt;&lt;/a&gt;
</pre><p>&#8230;you could easily code the following:</p><pre class="js">
var modal = new LightFace.Image();
$$('a[rel="lightface"]').addEvent('click',function() {
	modal.load(this.get('src'),this.get('alt'));
});
</pre><p>LightFace does not internally look for links with specific rel attributes.  My opinion is that those techniques are bad practice.</p><h2>LightFace.IFrame</h2><p>LightFace.IFrame provides a simple method for loading content from within an IFrame.  No thrills here, but the original LightFace class has been modified to look more elegant.  An example usage would be:</p><pre class="js">
var modal = new LightFace.IFrame({ 
	height:400, 
	width:800, 
	url: 'http://google.com', 
	title: 'Google!' 
}).addButton('Close', function() { 
	light.close(); 
},true).open();
</pre><p>I recommend setting a fixed height and width when creating LightFace.IFrame instances.</p><h2>LightFace.Static</h2><p>All LightFace classes automatically size and center the modal dialog. LightFace.Static bucks the trend by allowing for absolute positioning of the lightbox so you can place dialog anywhere you&#8217;d like!  Provide x and y coordinates to place the LightFace and it will appear exactly where you would like it to, plus offsets provided within the instance options:</p><pre class="js">
//Create context menu
var contextFace = new LightFace.Static({
	title: 'Context',
	content: 'Hello!',
	width: 80,
	height: 100
});

//Open when context-link is clicked
document.id('context-link').addEvent('click',function(e){
	if(e) e.stop();
	contextFace.open(false,e.page.x,e.page.y);
});

//Close if clicked outside
var closer = function(e) {
	var parent = document.id(contextFace).getParent('.lightface');
	if(e.target != parent &#038;&#038; !parent.contains(e.target)) {
		contextFace.close();
	}
};
document.id(document.body).addEvent('click',closer);
</pre><p>LightFace.Static is a perfect candidate for your next context menu or &#8220;toaster&#8221; functionality.</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/lightface.php" class="demo">View Demos</a> <a
href="http://mootools.net/forge/p/lightface" class="demo">Download LightFace</a> <a
href="http://davidwalsh.name/js/lightface" class="demo">Updated Documentation</a><div
class="clear"></div></div><h2>More To&nbsp;Come!</h2><p>Look forward to more demos of how you can use LightFace in the future (like photo tagging).  In the mean time, please feel free to fork on GitHub to help me improve LightFace and file bug reports on <a
href="http://github.com/darkwing/LightFace/issues">LightFace issues</a>.</p><p><a
href="http://davidwalsh.name/facebook-lightbox">LightFace:  Facebook Lightbox for&nbsp;MooTools</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/facebook-lightbox/feed</wfw:commentRss> <slash:comments>185</slash:comments> </item> <item><title>Introducing MooTools&#160;HeatMap</title><link>http://davidwalsh.name/mootools-heatmap</link> <comments>http://davidwalsh.name/mootools-heatmap#comments</comments> <pubDate>Tue, 26 Oct 2010 14:05:17 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[AJAX]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5099</guid> <description><![CDATA[It&#8217;s often interesting to think about where on a given element, whether it be the page, an image, or a static DIV, your users are clicking.  With that curiosity in mind, I&#8217;ve created HeatMap: a MooTools class that allows you to detect, load, save, and display spots on a given area where a user has [...]<p><a
href="http://davidwalsh.name/mootools-heatmap">Introducing MooTools&nbsp;HeatMap</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></description> <content:encoded><![CDATA[<p><a
href="http://davidwalsh.name/dw-content/heat-map.php"><img
src="http://davidwalsh.name/dw-content/heatmap.jpg" alt="MooTools HeatMap" width="550" /></a></p><p>It&#8217;s often interesting to think about where on a given element, whether it be the page, an image, or a static DIV, your users are clicking.  With that curiosity in mind, I&#8217;ve created HeatMap: a MooTools class that allows you to detect, load, save, and display spots on a given area where a user has clicked.</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/heat-map.php" class="demo">View Demo</a> <a
href="http://mootools.net/forge/p/heatmap" class="demo">Download HeatMap</a><div
class="clear"></div></div><h2>The&nbsp;CSS</h2><p>There&#8217;s really only one CSS declaration you&#8217;ll need to make with HeatMap, and that&#8217;s the CSS class that represents how a spot should look.  A sample spot CSS class could look like:</p><pre class="css">
.heatmap-spot	{ 
	width:6px; 
	height:6px; 
	margin-top:-3px; 
	margin-left:-3px;
	-webkit-border-radius:4px; 
	-moz-border-radius:4px; 
	background:#fff; 
	position:absolute; /* important! */
	z-index:200; 
}
</pre><p>HeatMap was created to allow spot styling to look however you&#8217;d like.  Note that you&#8217;ll want to add negative margins to the spot depending on how large you create the spot.</p><h2>The MooTools&nbsp;JavaScript</h2><p>The class is relatively compact but grew larger than I had expected.  HeatMap allows for easy loading and saving of spots with minimal code.  Here&#8217;s the complete class:</p><pre class="js">
var HeatMap = new Class({
	options: {
		event: 'click',
		load: {
			// request settings here
		},
		method: 'get',
		save: {
			// request settings here
		},
		spotClass: 'heatmap-spot',
		zone: ''/*,
		onClick: $empty,
		onSpot: $empty
		*/
	},
	Implements: [Options,Events],
	initialize: function(element,options) {
		this.element = document.id(element).setStyle('position','relative');
		this.setOptions(options);
		this.newClicks = [];
		this.oldClicks = [];
		this.attachEvents();
	},
	attachEvents: function() {
		var self = this;
		this.clickEvent = function(e) {
			var obj = self.getRelativePosition(e.page.x,e.page.y);
			obj.spot = self.createSpot(obj.x,obj.y);
			self.newClicks.push(obj);
		};
		this.element.addEvent(this.options.event,this.clickEvent);
	},
	detachEvents: function() {
		this.element.removeEvent(this.options.event,this.clickEvent);
	},
	getRelativePosition: function(x,y) {
		var position = this.element.getPosition();
		return { x: x - position.x, y: y - position.y };
	},
	load: function() {
		if(!this.loadRequest) this.loadRequest = new Request.JSON(this.options.load);
		if(!this.options.load.onSuccess &#038;&#038; !this.loadSuccess) {
			this.loadSuccess = function(json) {
				json.each(function(click,i) {
					json[i].spot = this.createSpot(click.x,click.y);
					this.oldClicks.push(json[i]);
				},this);
			}.bind(this);
			this.loadRequest.addEvent('success',this.loadSuccess);
		}
		this.loadRequest[this.options.method]({
			load: 1,
			zone: this.options.zone
		});
		return this;
	},
	save: function(data) {
		if(!this.sendRequest) this.sendRequest = new Request.JSON(this.options.save);
		if(this.newClicks.length) {
			this.sendRequest.addEvent('success',function() {
				this.newClicks.each(function(click) {
					this.oldClicks.push(this.createSpot({ x: click.x, y:click.y }));
				},this);
				this.newClicks = [];
			}.bind(this));
			this.sendRequest[this.options.method]({
				save: 1,
				zone: this.options.zone,
				data: this.newClicks
			});
		}
		return this;
	},
	createSpot: function(x,y) {
		var spot = new Element('div',{
			'class': this.options.spotClass,
			styles: {
				top: y.toInt(),
				left: x.toInt()
			}
		}).inject(this.element);
		this.fireEvent('spot',[spot,x,y]);
		return spot;
	}
});
</pre><p>Arguments for HeatMap include:</p><ul><li><strong>element:</strong> the element with which to listen for clicks on</li><li><strong>options:</strong> options for the class instance</li></ul><p>Options for HeatMap include:</p><ul><li><strong>event:</strong> <em>(string, defaults to event) </em> the event to listen for &#8212; defaults to click</li><li><strong>load:</strong> <em>(object, defaults to {})</em> the Request.JSON options object for loading spots</li><li><strong>method:</strong> <em>(string, defaults to &#8220;get&#8221;)</em> the Request.JSON request type</li><li><strong>save:</strong> <em>(object, defaults to {}) </em> the Request.JSON options object for saving spots</li><li><strong>spotClass:</strong> <em>(string, defaults to &#8216;heatmap-spot&#8217;)</em> the CSS class for styling a spot</li><li><strong>zone:</strong> <em>(string, defaults to &#8221;) </em>the &#8220;zone&#8221; by which the click will be saved under; especially important if more than one spot is one the page.</li></ul><p>Events for HeatMap include:</p><ul><li><strong>onSpot:</strong> fires when a spot is created.</li></ul><p>A relatively simple class.  The class could have more complexity but I&#8217;ve chosen to keep it simple for iteration one.</p><h2>HeatMap&nbsp;Usage</h2><p>Using HeatMap is as simple as this:</p><pre class="js">
/* usage */
window.addEvent('domready',function() {
	map = new HeatMap('ricci-map',{
		zone: 'cricci',
		save: { url: 'heat-map.php' },
		load: { url: 'heat-map.php' },
		onSpot: function(spot) {
			spot.setStyle('opacity',0).fade(1);
		}
	});
	document.id('loader').addEvent('click',function() {
		map.load();
	});
	document.id('saver').addEvent('click',function() {
		map.save();
	});
});
</pre><p>Much simpler than you had probably imagined!  I&#8217;d recommend using <code>click</code> as the event &#8212; using other types of events could be confusing to users and could result in massive amounts of data for <code>mouseenter</code> events.</p><h2>The MySQL&nbsp;Table</h2><p>My MySQL table looks as follows:</p><pre class="sql">
CREATE TABLE `example_heatmap` (
  `click_id` mediumint(6) NOT NULL auto_increment,
  `zone` varchar(60) NOT NULL default '',
  `x` smallint(5) NOT NULL default '0',
  `y` smallint(5) NOT NULL default '0',
  `date_clicked` datetime NOT NULL,
  PRIMARY KEY  (`click_id`)
) ENGINE=MyISAM AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
</pre><p>How you choose to set up the SQL side of this is entirely up to you.</p><h2>The PHP Script&nbsp;</h2><p>A few thing I&#8217;d like to point out about the server-side handling of HeatMap:</p><ol><li>You can use any server-side language to facilitate the loading of saving of spots &#8212; I simply used PHP because it&#8217;s what I&#8217;m most familiar with.</li><li>Save your complaints about my usage of PHP&#8217;s native mysql functions and the lack of validation &#8212; my focus with this post is the JavaScript class.</li></ol><p>Without further adieu, here&#8217;s a PHP solution for saving and loading spots:</p><pre class="php">
/* load  */
if(isset($_GET['load'])) {
	
	/* vars */
	$spots = array();
	
	/* connect to the db */
	$connection = mysql_connect('localhost','dbuser','dbpass');
	mysql_select_db('dbname',$connection);
	
	/* get spots */
	$query = 'SELECT * FROM example_heatmap WHERE zone = \''.mysql_escape_string($_GET['zone']).'\' LIMIT 2000';
	$result = mysql_query($query,$connection);
	while($record = mysql_fetch_assoc($result)) {
		$spots[] = $record;
	}
	
	/* close db connection */
	mysql_close($connection);
	
	/* return result */
	$json = json_encode($spots);
	echo $json;
	die();
}
/* save */
elseif(isset($_GET['save']) &#038;&#038; isset($_GET['data']) &#038;&#038; count($_GET['data'])) {
	
	/* vars */
	$query = 'INSERT INTO example_heatmap (zone,x,y,date_clicked) VALUES ';
	$queryRecords = array();
	$records = 0;
	
	/* connect to the db */
	$connection = mysql_connect('localhost','dbuser','dbpass');
	mysql_select_db('dbname',$connection);
	
	/* save! */
	foreach($_GET['data'] as $data) {
		$queryRecords[] =  '(\''.mysql_escape_string($_GET['zone']).'\','.mysql_escape_string($data['x']).','.mysql_escape_string($data['y']).',NOW())';
		$records++;
	}
	
	/* execute query, close */
	$query.= implode(',',$queryRecords);
	mysql_query($query,$connection);
	mysql_close($connection);
	
	/* return result */
	die(count($records));
}
</pre><p>I prefer to use one script for both the saving and loading of spots &#8212; using one script cuts down on the number of files you need and the logic to handle multiple functionality isn&#8217;t difficult to organize within that one file.</p><div
class="actions"> <a
href="http://davidwalsh.name/dw-content/heat-map.php" class="demo">View Demo</a> <a
href="http://mootools.net/forge/p/heatmap" class="demo">Download HeatMap</a><div
class="clear"></div></div><h2>Bring the&nbsp;Heat!</h2><p>MooTools HeatMap is something I find incredibly fun.  You could use HeatMap on an image, a static DIV, or the entire body.  If you don&#8217;t want the user to see spots and simply want to track their clicks, you could hide spots and periodically save clicks.  Have fun with this class and let me know if you have suggestions!</p><p><a
href="http://davidwalsh.name/mootools-heatmap">Introducing MooTools&nbsp;HeatMap</a> is a post from: <a
href="http://davidwalsh.name">David Walsh :: Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</a></p> ]]></content:encoded> <wfw:commentRss>http://davidwalsh.name/mootools-heatmap/feed</wfw:commentRss> <slash:comments>27</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced (User agent is rejected)
Database Caching 2/55 queries in 0.023 seconds using disk: basic
Object Caching 1463/1560 objects using disk: basic

Served from: davidwalsh.name @ 2012-02-08 13:29:27 -->
