<?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; Markup</title> <atom:link href="http://davidwalsh.name/tutorials/html/feed" rel="self" type="application/rss+xml" /><link>http://davidwalsh.name</link> <description>Legendary scribbles about JavaScript, HTML5, AJAX, PHP, CSS, and ∞.</description> <lastBuildDate>Sun, 20 May 2012 22:40:48 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.2</generator> <item><title>JavaScript Canvas Image&#160;Conversion</title><link>http://davidwalsh.name/convert-canvas-image</link> <comments>http://davidwalsh.name/convert-canvas-image#comments</comments> <pubDate>Tue, 08 May 2012 15:53:56 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5426</guid> <description><![CDATA[At last week&#8217;s Mozilla WebDev Offsite, we all spent half of the last day hacking on our future Mozilla Marketplace app. One mobile app that recently got a lot of attention was Instagram, which sold to Facebook for the bat shit crazy price of one billion dollars. Since I wouldn&#8217;t mind having a bill in [...]<p><a
href="http://davidwalsh.name/convert-canvas-image">JavaScript Canvas Image&nbsp;Conversion</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>At last week&#8217;s Mozilla WebDev Offsite, we all spent half of the last day hacking on our future Mozilla Marketplace app.  One mobile app that recently got a lot of attention was Instagram, which sold to Facebook for the <del>bat shit crazy</del> price of one billion dollars.  Since I wouldn&#8217;t mind having a bill in my back account, I decided to create an Instagram-style app (which I&#8217;ll share with you in the future).  This post details how you can convert an image to canvas and convert a canvas back to an image.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/convert-canvas-image.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>Convert an Image to Canvas with&nbsp;JavaScript</h2><p>To convert an image to canvas, you use a canvas element&#8217;s context&#8217;s <code>drawImage</code> method:</p><pre class="js">
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
	var canvas = document.createElement("canvas");
	canvas.width = image.width;
	canvas.height = image.height;
	canvas.getContext("2d").drawImage(image, 0, 0);

	return canvas;
}
</pre><p>The <code>0, 0</code> arguments map to coordinates on the canvas where the image data should be placed.</p><h2>Convert Canvas to an Image with&nbsp;JavaScript</h2><p>Assuming modifications to the image have been made, you can easily convert the canvas data to image data with the following snippet:</p><pre class="js">
// Converts canvas to an image
function convertCanvasToImage(canvas) {
	var image = new Image();
	image.src = canvas.toDataURL("image/png");
	return image;
}
</pre><p>The code above magically converts the canvas to a PNG data URI!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/convert-canvas-image.php" class="demo">View Demo</a><div
class="clear"></div></div><p>Alas, converting an image to canvas and canvas to an image is probably much easier than you think.  In future posts, I&#8217;ll detail how you can apply different image filters to your canvased image.  In the mean time, start buying fancy cars and houses with the future billion you&#8217;ll have!</p><p><a
href="http://davidwalsh.name/convert-canvas-image">JavaScript Canvas Image&nbsp;Conversion</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/convert-canvas-image/feed</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>DNS&#160;Prefetching</title><link>http://davidwalsh.name/dns-prefetching</link> <comments>http://davidwalsh.name/dns-prefetching#comments</comments> <pubDate>Tue, 17 Apr 2012 21:55:38 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5415</guid> <description><![CDATA[Despite anchor tags having HREF attributes which lead to other host names, browsers do not execute DNS lookups on those domains. Content prefetching can be invaluable in speeding up your websites, but did you know that you can also implement DNS prefetching? It&#8217;s as easy as simple LINK element: &#60;link rel="dns-prefetch" href="//somehost.tld" /&#62; This technique [...]<p><a
href="http://davidwalsh.name/dns-prefetching">DNS&nbsp;Prefetching</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>Despite anchor tags having <code>HREF</code> attributes which lead to other host names, browsers do not execute DNS lookups on those domains. Content prefetching can be invaluable in speeding up your websites, but did you know that you can also implement DNS prefetching?  It&#8217;s as easy as simple <code>LINK</code> element:</p><pre class="html">
&lt;link rel="dns-prefetch" href="//somehost.tld" /&gt;
</pre><p>This technique can be very useful when your website links to related host names.  Take Twitter for example;  they implement two DNS prefetches:</p><pre class="html">
&lt;link rel="dns-prefetch" href="https://si0.twimg.com" /&gt;
&lt;link rel="dns-prefetch" href="https://api.twitter.com" /&gt;
</pre><p>I&#8217;d be willing to bet that most of you didn&#8217;t know this tag existed.  It&#8217;s an interesting idea with a very simple execution.  What do you think?  Do you manage websites that prefetching could be helpful for?</p><p><a
href="http://davidwalsh.name/dns-prefetching">DNS&nbsp;Prefetching</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/dns-prefetching/feed</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>HTML5 Context&#160;Menus</title><link>http://davidwalsh.name/html5-context-menu</link> <comments>http://davidwalsh.name/html5-context-menu#comments</comments> <pubDate>Fri, 24 Feb 2012 01:54:54 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[HTML5]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5391</guid> <description><![CDATA[Update: These context menus display even when JavaScript is disabled; so best practice will be to create these menu structures via JavaScript. Mass innerHTML injection can be used or basic DOM node injection. One of the hidden gems within the HTML5 spec is context menus. The HTML5 context menu spec allows developers to create custom [...]<p><a
href="http://davidwalsh.name/html5-context-menu">HTML5 Context&nbsp;Menus</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[<div
class="update"><p><strong>Update:</strong> These context menus display even when JavaScript is disabled;  so best practice will be to create these menu structures via JavaScript.  Mass innerHTML injection can be used or basic DOM node injection.</p></div><a
href="http://davidwalsh.name/dw-content/html5-context-menu.php"><img
src="http://davidwalsh.name/dw-content/firefox-contextmenu.png" alt="Firefox Context Menu" class="image" /></a><p>One of the hidden gems within the <a
href="http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus">HTML5 spec is context menus</a>.  The HTML5 context menu spec allows developers to create custom context menus for given blocks within simple menu and menuitem elements.  The menu information lives right within the page so there&#8217;s no need to <a
href="http://davidwalsh.name/firefox-toolbar">create a custom plugin</a>.  Let me show you how you can create your own custom context menus from basic HTML!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/html5-context-menu.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>The&nbsp;HTML</h2><p>Let&#8217;s first start by defining a block of HTML and assigning the ID of the menu nodes to be used:</p><pre class="html">
&lt;section contextmenu="mymenu"&gt;
	&lt;!-- 
		For the purpose of cleanliness, 
		I'll put my menu inside the element that will use it 
	--&gt;
&lt;/section&gt;
</pre><p>With the block defined, now it&#8217;s time to create the additional context menu items for the given block:</p><pre class="html">
&lt;menu type="context" id="mymenu"&gt;
	&lt;menuitem label="Refresh Post" onclick="window.location.reload();" icon="/images/refresh-icon.png"&gt;&lt;/menuitem&gt;
	&lt;menuitem label="Skip to Comments" onclick="window.location='#comments';" icon="/images/comment_icon.gif"&gt;&lt;/menuitem&gt;
	&lt;menu label="Share on..." icon="/images/share_icon.gif"&gt;
		&lt;menuitem label="Twitter" icon="/images/twitter_icon.gif" onclick="goTo('//twitter.com/intent/tweet?text=' + document.title + ':  ' + window.location.href);"&gt;&lt;/menuitem&gt;
		&lt;menuitem label="Facebook" icon="/images/facebook_icon16x16.gif" onclick="goTo('//facebook.com/sharer/sharer.php?u=' + window.location.href);"&gt;&lt;/menuitem&gt;
	&lt;/menu&gt;
&lt;/menu&gt;
</pre><p>With a base menu tag with the type of context and id to match the <code>context</code> attribute for the blog it&#8217;s to be used for, menu items or submenus may be created.  Menu items may have <code>label</code>, <code>icon</code>, and <code>onclick</code> attributes to represent design and actions.  Actions can have predefined functions or inline javascript code, just as any element can.  Multiple parents can use the same menu, so no need to repeat the same menus.</p><p><em>This is where I&#8217;d insert more detail, but this is so damn easy that there&#8217;s no point in boring you&#8230;</em></p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/html5-context-menu.php" class="demo">View Demo</a><div
class="clear"></div></div><p>Mozilla Firefox is currently the only browser to support this API.  I wouldn&#8217;t consider adding context menus a critical necessity but the ability to do so is quite nice, and I plan to add them to my blog soon.  This API is the epitome of &#8220;used for enhancement and wont harm&#8221;.  My &#8220;share&#8221; example is fairly basic and supplemental;  have an scenario which the contextmenu API may be more useful?  Share!</p><p><a
href="http://davidwalsh.name/html5-context-menu">HTML5 Context&nbsp;Menus</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/html5-context-menu/feed</wfw:commentRss> <slash:comments>18</slash:comments> </item> <item><title>Image Data URIs with&#160;PHP</title><link>http://davidwalsh.name/data-uri-php</link> <comments>http://davidwalsh.name/data-uri-php#comments</comments> <pubDate>Mon, 30 Jan 2012 16:42:40 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[Markup]]></category> <category><![CDATA[PHP]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5377</guid> <description><![CDATA[If you troll page markup like me, you&#8217;ve no doubt seen the use of data URI&#8217;s within image src attributes. Instead of providing a traditional address to the image, the image file data is base64-encoded and stuffed within the src attribute. Doing so saves a network request for each image, and if you&#8217;re the paranoid [...]<p><a
href="http://davidwalsh.name/data-uri-php">Image Data URIs with&nbsp;PHP</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 troll page markup like me, you&#8217;ve no doubt seen the use of data URI&#8217;s within image src attributes.  Instead of providing a traditional address to the image, the image file data is base64-encoded and stuffed within the src attribute.  Doing so saves a network request for each image, and if you&#8217;re the paranoid type, can prevent exposure of directory paths.  Since creating data URIs is incredibly easy, let me show you how to do it with PHP.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/data-uri-php.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>The&nbsp;PHP</h2><p>Start by reading in the image using <code>file_get_contents</code> (or any other PHP method you&#8217;d like), then convert the image to base64 using <code>base64_encode</code>:</p><pre class="php">
// A few settings
$image = 'cricci.jpg';

// Read image path, convert to base64 encoding
$imageData = base64_encode(file_get_contents($image));

// Format the image SRC:  data:{mime};base64,{data};
$src = 'data: '.mime_content_type($image).';base64,'.$imageData;

// Echo out a sample image
echo '<img src="',$src,'" />';
</pre><p>With the image data in base64 format, the last step is placing that data within the data URI format, including the image&#8217;s MIME type.  This would make for a good function:</p><pre class="php">
function getDataURI($image, $mime = '') {
	return 'data: '.(function_exists('mime_content_type') ? mime_content_type($image) : $mime).';base64,'.base64_encode(file_get_contents($image));
}
</pre><div
class="actions"><a
href="http://davidwalsh.name/dw-content/data-uri-php.php" class="demo">View Demo</a><div
class="clear"></div></div><p>The thing to remember is that IE7 and lower don&#8217;t support data URIs, so keep that in mind if you&#8217;re considering switching from image paths to data URIs!</p><p><a
href="http://davidwalsh.name/data-uri-php">Image Data URIs with&nbsp;PHP</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/data-uri-php/feed</wfw:commentRss> <slash:comments>17</slash:comments> </item> <item><title>Facebook Tooltip HTML and&#160;CSS</title><link>http://davidwalsh.name/facebook-tooltip</link> <comments>http://davidwalsh.name/facebook-tooltip#comments</comments> <pubDate>Tue, 08 Nov 2011 14:46:23 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[APIs]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5329</guid> <description><![CDATA[Facebook recently implemented a new, lighter tooltip. I say the tooltip is lighter because it seems a lot quicker and more elegant than their previous effort. I took a few moments to grab the HTML structure and CSS rules to see how they did it. The&#160;HTML The tooltip structure consists of five elements: {content here}{content [...]<p><a
href="http://davidwalsh.name/facebook-tooltip">Facebook Tooltip HTML and&nbsp;CSS</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/fb-tip.png" alt="Facebook Tooltip" class="image" /><p>Facebook recently implemented a new, lighter tooltip.  I say the tooltip is lighter because it seems a lot quicker and more elegant than their previous effort.  I took a few moments to grab the HTML structure and CSS rules to see how they did it.</p><h2>The&nbsp;HTML</h2><p>The tooltip structure consists of five elements:</p><pre class="html">
<div class="uiContextualDialogPositioner uiContextualDialogLeft" style="top: 20px; left: 600px;">
	<div class="uiOverlay uiContextualDialog uiOverlayArrowRight" style="width: 347px; top: 0px; ">
		<div class="uiOverlayContent">
			<div class="uiOverlayContentHolder">
				{content here}{content here}{content here}{content here}{content here}{content here}{content here}{content here}{content here}
			</div>
		</div>
		<div class="uiOverlayArrow" style="top: 15px; margin-top: 0px;"></div>
	</div>
</div>
</pre><p>The root element dictates the position of the tooltip (which is most likely injected to the body).  The sole child element controls the width of the tooltip.  That element contains two elements:  the content container and the and the arrow node (which I&#8217;ve changed from an I element to a DIV).  The last, innermost DIV element will hold the content and provide padding.</p><h2>The&nbsp;CSS</h2><p>The CSS to create the tooltip layout is actually very minimal:</p><pre class="css">
body {
	font-size: 11px;
	font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
	color: #333;
	line-height: 1.28; 
	text-align: left;
	direction: ltr;
}

.uiContextualDialogPositioner, .uiContextualDialogPositioner .uiContextualDialog {
	position: absolute;
	z-index: 200;
}

.uiContextualDialogLeft .uiContextualDialog {
	right: 0;
}

.uiOverlayArrowRight {
	padding-right: 10px;
}

.uiOverlay {
	position: relative;
	z-index: 200;
}

.uiContextualDialog, .uiContextualDialog:focus {
	outline: 0 solid transparent;
}

.uiOverlayContent {
	background: white;
	border: 1px solid #8C8C8C;
	border: 1px solid rgba(0, 0, 0, .45);
	border-bottom: 1px solid #666;
	-moz-box-shadow: 0 3px 8px rgba(0, 0, 0, .3);
	-webkit-box-shadow: 0 3px 8px rgba(0, 0, 0, .3);
	box-shadow: 0 3px 8px rgba(0, 0, 0, .3);
	position: relative;
}

.uiOverlayContentHolder {
	padding: 10px;
}

.uiOverlayArrow {
	position: absolute;
	overflow: hidden;
}

.uiOverlayArrowRight .uiOverlayArrow {
	background-image: url(sprite.png);
	background-repeat: no-repeat;
	background-position: -177px -309px;
	height: 16px;
	right: 2px;
	width: 9px;
}
</pre><p>The content pane contains the majority of the CSS rules, include the box-shadow and border, both of which use rgba color for a more detailed effect.  Showing and hiding of the tooltip may be done via CSS key-frames or JavaScript &#8212; the choice would be up to the individual implementing the tooltip.</p><h2>Why Show&nbsp;This?</h2><p>Two reasons.  The first is that I appreciate well-coded features like this.  The second, more important reason, is that I&#8217;ll be creating a JavaScript-powered version of this functionality which accounts for content size, position on page, stacking/z-index management, etc.  Do I create as a jQuery and MooTools plugin?  Do I create it as a standalone JavaScript project.  Let me know your thoughts!</p><p><a
href="http://davidwalsh.name/facebook-tooltip">Facebook Tooltip HTML and&nbsp;CSS</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-tooltip/feed</wfw:commentRss> <slash:comments>25</slash:comments> </item> <item><title>Control Element Outline Position with&#160;outline-offset</title><link>http://davidwalsh.name/outline-offset</link> <comments>http://davidwalsh.name/outline-offset#comments</comments> <pubDate>Thu, 27 Oct 2011 15:52:59 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5325</guid> <description><![CDATA[I was recently working on a project which featured tables that were keyboard navigable so obviously using cell outlining via traditional tabIndex=0 and element outlines was a big part of allowing the user navigate quickly and intelligently. Unfortunately I ran into a Firefox 3.6 bug where the element outline forced a horizontal scrollbar within its [...]<p><a
href="http://davidwalsh.name/outline-offset">Control Element Outline Position with&nbsp;outline-offset</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>I was recently working on a project which featured tables that were keyboard navigable so obviously using cell outlining via traditional <code>tabIndex=0</code> and element outlines was a big part of allowing the user navigate quickly and intelligently.  Unfortunately I ran into a Firefox 3.6 bug where the element outline forced a horizontal scrollbar within its parent, causing the table to look&#8230;.gross.  After a bit of research, I discovered the <a
href="https://developer.mozilla.org/en/CSS/outline-offset" rel="nofollow"><code>outline-offset</code> CSS property</a>.  Let me show you what it is and how it fixed the outline bug!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/outline-offset.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>The&nbsp;CSS</h2><p>Assuming you have an <a
href="http://davidwalsh.name/tabindex-focus">element that&#8217;s focusable (usually via <code>tabIndex</code>)</a>, the CSS to adjust the outline is as simple as you would expect it to be:</p><pre class="css">
.tableWidget td {
	outline-offset: 2px;
}
</pre><p>A positive <code>outline-offset</code> moves the offset further outside the element, whereas you can create an inner outline using negative <code>outline-offset</code>:</p><pre class="css">
.tableWidget td {
	outline-offset: -1px;
}
</pre><p>In the case of the bug I mentioned above, using a negative <code>outline-offset</code> fixed the problem because the outline stays within element.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/outline-offset.php" class="demo">View Demo</a><div
class="clear"></div></div><p>I wasn&#8217;t aware of the <code>outline-offset</code> property before coming across this issue and I&#8217;m certainly glad I know about it now.  Removing the outline property is never a good idea because usability and accessibility, so the ability to control its placement relative to the element border is very valuable.</p><p><a
href="http://davidwalsh.name/outline-offset">Control Element Outline Position with&nbsp;outline-offset</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/outline-offset/feed</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>DOM Manipulation with&#160;put-selector</title><link>http://davidwalsh.name/put-selector</link> <comments>http://davidwalsh.name/put-selector#comments</comments> <pubDate>Mon, 17 Oct 2011 15:12:08 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5316</guid> <description><![CDATA[DOM node manipulation is a big part of the web today; just look at how popular the jQuery JavaScript framework has gotten. The ability to easily work with the DOM allows us to do a lot of work in a little bit of code. Thanks to a new JavaScript module by Dojo Toolkit developer Kris [...]<p><a
href="http://davidwalsh.name/put-selector">DOM Manipulation with&nbsp;put-selector</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>DOM node manipulation is a big part of the web today;  just look at how popular the jQuery JavaScript framework has gotten.  The ability to easily work with the DOM allows us to do a lot of work in a little bit of code.  Thanks to a new JavaScript module by Dojo Toolkit developer Kris Zyp, working with the DOM just got a lot more compact.  With a name as short as the syntax itself, <a
href="https://github.com/kriszyp/put-selector" rel="nofollow">put</a> could make you change the way you work with the DOM.</p><h2>Creating DOM&nbsp;Nodes</h2><p>Creating new nodes is just about as simple as it gets:</p><pre class="js">
// Create an empty DIV
var div = put("div");
</pre><p>Boom, there&#8217;s your new DIV element!  And if you want your new code to have a few CSS classes:</p><pre class="js">
// Create a DIV with some CSS classes
var div2 = put("div.classOne.classTwo");
</pre><p>How about creating nodes with attributes?</p><pre class="js">
// Create a DIV with some CSS classes and attributes
var div3 = put("div.classOne.classTwo[title=Hover over me][style=display:inline-block]");
</pre><p>The syntax for inserting the DOM node is a bit different because the parent then becomes the first argument in the put signature:</p><pre class="js">
// Create an INPUT with an ID and an attribute, place it into the body
// The body text goes into the third argument;  not(!) innerHTML, just text
var div4 = put(document.body, "input[type=text]#myInput", "This is inner html");
</pre><h2>Manipulating Existing DOM&nbsp;Nodes</h2><p>Manipulating DOM nodes is actually very much like creating the nodes themselves:</p><pre class="js">
var myInput = document.getElementById("myInput");
put(myInput, ".anotherClass");

// Add CSS classes attributes to the element
put(myInput, "[placeholder=first name][required=true][title=first name element].yetAnotherClass");
</pre><p>Just remove the tagName and you can modify the node.</p><h2>Node, Class, and Attribute&nbsp;Deletion</h2><p>The &#8220;!&#8221; character is meaningful in that it represents deletion within put.  Let&#8217;s remove a few classes and attributes from a given node, then let&#8217;s remove the node itself:</p><pre class="js">
// Remove a CSS class from the INPUT element
put(myInput, "!anotherClass");

// Remove attributes from the INPUT element
put(myInput, "[!required][!title]");

// Delete a node!
put(myInput, "!");
</pre><p>The syntax for deletion is short but sweet.  The only criticism I have is that if this could create a maintenance problem if developers are not experience with put.</p><h2>Creating and Managing Child&nbsp;Nodes</h2><p>Creating childNodes for an existing node is another functionality made easier by put:</p><pre class="js">
// Create a base node so we can add nodes around it
var baseNode = put(document.body, "div#baseNode");

// Add two DIVs *after* the baseNode
put(baseNode, "+div +div");

// Add a SPAN element *before* the baseNode
put(baseNode, "-span");

// Create the parent list element 
var ul = put(document.body, "ul");

// Create a child list item
var firstItem = put(ul, "li");
</pre><h2>Moving and ReParenting&nbsp;Nodes</h2><p>Reparenting and moving elements within the DOM is another basic task that, when you use JavaScript&#8217;s native functions, takes far too much code.  Let&#8217;s have a look at moving nodes around with put:</p><pre class="js">
// Move one node to another parent
// parent > child
put(ul, ">", firstItem);

// Put one element after the first
put(div1, "+", div2);
</pre><p>A simple series of symbols designates where within the node try an element should go!</p><h2>More Traditional Node Property&nbsp;Management</h2><p>For those who prefer a more verbose element creation syntax, put-selector provides that as well:</p><pre class="js">
var span = put(parent, "span", {
	title: "Hover over me!",
	style: "font-style: italic;"
});
</pre><p>Kris&#8217; tool is great in that it&#8217;s ultra-efficient, modular, and easy to learn.  On the other side, it&#8217;s possible that the syntax may be a bit <em>too</em> compact for persons try to maintain an application that they didn&#8217;t write.  What do you think of put?  An easy to use utility or a &#8220;simple but complicated&#8221; resource?  If I&#8217;m honest, I&#8217;m teetering on the fence &#8212; maybe you can convince me?</p><p><a
href="http://davidwalsh.name/put-selector">DOM Manipulation with&nbsp;put-selector</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/put-selector/feed</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>Create a Photo Stack Effect with Pure CSS Animations or&#160;MooTools</title><link>http://davidwalsh.name/photo-stack</link> <comments>http://davidwalsh.name/photo-stack#comments</comments> <pubDate>Tue, 19 Jul 2011 14:07:44 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[Markup]]></category> <category><![CDATA[MooTools]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5269</guid> <description><![CDATA[My favorite technological piece of Google Plus is its image upload and display handling.  You can drag the images from your OS right into a browser&#8217;s DIV element, the images upload right before your eyes, and the albums page displays a sexy photo deck animation when you hover over them.  Outstanding!  I&#8217;ve researched the effect [...]<p><a
href="http://davidwalsh.name/photo-stack">Create a Photo Stack Effect with Pure CSS Animations or&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[<a
href="http://davidwalsh.name/dw-content/photo-stack.php"><img
src="http://davidwalsh.name/dw-content/photo-stack.png" alt="CSS Animations MooTools" class="image" /></a><p>My favorite technological piece of Google Plus is its image upload and display handling.  You can drag the images from your OS right into a browser&#8217;s DIV element, the images upload right before your eyes, and the albums page displays a sexy photo deck animation when you hover over them.  Outstanding!  I&#8217;ve researched the effect a bit and was able to pull it off with both pure CSS and some MooTools JavaScript.</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/photo-stack.php" class="demo">View Demo</a><div
class="clear"></div></div><h2>Initial&nbsp;Setup</h2><p>The HTML setup is quite simple:</p><pre class="html">
&lt;a href="" class="album"&gt;
	&lt;img src="http://davidwalsh.name/dw-content/gplus/photo4.png" class="photo1" /&gt;
	&lt;img src="http://davidwalsh.name/dw-content/gplus/photo1.png" class="photo2" /&gt;
	&lt;img src="http://davidwalsh.name/dw-content/gplus/photo2.png" class="photo3" /&gt;
&lt;/a&gt;
</pre><p>I&#8217;ve used an <code>A</code> elements so that we can also play the animation with focus via tab.  With the images in place, we need to style the <code>A</code> and images themselves:</p><pre class="css">
/* the A tag */
.album {
	position: relative;
	z-index: 5;
	width: 250px;
	vertical-align: top;
	display:block;
}

/* the images */
.album img { 
	position: absolute;
	top: 0;
	left: 0;
	border: 5px solid #f3f3f3;
	box-shadow: 1px 1px 2px #666;
	-webkit-box-shadow: 1px 1px 2px #666;
	-moz-box-shadow: 1px 1px 2px #666;
	width:250px;
	height:250px;
	display:block;
}
</pre><p>These styles act as a base to the CSS and MooTools methods of animation.</p><h2>The CSS&nbsp;Version</h2><p>Both the CSS-only and MooTools versions will rely on browser-provided CSS3 animations via transforms.  The transforms we&#8217;ll be using are rotate, translate, and scale.  The scale will be kept relatively small so that the magnification isn&#8217;t abrupt.</p><p>The first step is getting our <code>@-keyframes</code> properties set up so that we can use the animations later. The following CSS assumes there will be 3 photos and user Google&#8217;s animation values:</p><pre class="css">

/* first image:  webkit */
@-webkit-keyframes image1 {
	0%   { -webkit-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -webkit-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1); }
}

/* first image:  firefox */
@-moz-keyframes image1 {
	0%   { -moz-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -moz-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1); }
}

/* first image:  opera */
@-o-keyframes image1 {
	0%   { -o-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1); }
}

/* second image:  webkit */
@-webkit-keyframes image2 {
	0%   { -webkit-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -webkit-transform: rotate(0deg) translate(0, -3px) scale(1.1); }
}
/* second image:  firefox */
@-moz-keyframes image2 {
	0%   { -moz-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -moz-transform: rotate(0deg) translate(0, -3px) scale(1.1); }
}
/* second image:  opera */
@-o-keyframes image2 {
	0%   { -o-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -o-transform: rotate(0deg) translate(0, -3px) scale(1.1); }
}

/* third image:  webkit */
@-webkit-keyframes image3 {
	0%   { -webkit-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -webkit-transform: rotate(6deg) translate(100px, -3px) scale(1.1); }
}

/* third image:  firefox */
@-moz-keyframes image3 {
	0%   { -moz-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -moz-transform: rotate(6deg) translate(100px, -3px) scale(1.1); }
}

/* third image:  opera */
@-o-keyframes image3 {
	0%   { -o-transform: rotate(0deg) translate(0, 0) scale(1.0); }
	100% { -o-transform: rotate(6deg) translate(100px, -3px) scale(1.1); }
}
</pre><p>With the keyframes ready, it&#8217;s time to implement the CSS settings within the selectors themselves:</p><pre class="css">
/* first image animation properties */
.album:hover .photo1, .album:focus .photo1 {

	/* webkit animation properties */
	-webkit-animation-name: image1;
	-webkit-animation-duration: .2s;
	-webkit-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
	
	/* firefox animation properties */
	-moz-animation-name: image1;
	-moz-animation-duration: .2s;
	-moz-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
	
	/* opera animation properties */
	-o-animation-name: image1;
	-o-animation-duration: .2s;
	-o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
	
	/* microsoft animation properties */
	-ms-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
}

/* second image animation properties */
.album:hover .photo2, .album:focus .photo2 {

	/* webkit animation properties */
	-webkit-animation-name: image2;
	-webkit-animation-duration: .2s;
	-webkit-transform: rotate(0deg) translate(0, -3px) scale(1.1);
	
	/* firefox animation properties */
	-moz-animation-name: image2;
	-moz-animation-duration: .2s;
	-moz-transform: rotate(0deg) translate(0, -3px) scale(1.1);
	
	/* opera animation properties */
	-o-animation-name: image2;
	-o-animation-duration: .2s;
	-o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
	
	/* microsoft animation properties */
	-ms-transform: rotate(0deg) translate(0, -3px) scale(1.1);
}

/* third image animation properties */
.album:hover .photo3, .album:focus .photo3 {

	/* webkit animation properties */
	-webkit-animation-name: image3;
	-webkit-animation-duration: .2s;
	-webkit-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
	
	/* firefox animation properties */
	-moz-animation-name: image3;
	-moz-animation-duration: .2s;
	-moz-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
	
	/* opera animation properties */
	-o-animation-name: image3;
	-o-animation-duration: .2s;
	-o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
	
	/* microsoft animation properties */
	-ms-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
}
</pre><p>The <code>:hover</code> state signals showtime, and the animation is wonderful.  You&#8217;ll see that I&#8217;ve duplicated the 100% setting within the <code>:hover</code> state as well, and that ensures the elements stay at their destination transformation after the animation is playing <em>(otherwise the elements would abruptly revert to original state one the animation was done playing).</em></p><p>While this animation looks great, and requires no JavaScript, the abruptness in return to original state is slightly off-putting.  Time for some MooTools magic.</p><h2>UPDATE:  CSS Transformations&nbsp;FTW</h2><p>Using CSS transformations instead of animations, we can create a complete CSS-only solution!</p><pre class="css">
/* the A tag */
.album {
  position: relative;
  z-index: 5;
  width: 250px;
  vertical-align: top;
  display:block; 
}

/* the images */
.album img { 
  position: absolute;
  top: 0;
  left: 0;
  border: 5px solid #f3f3f3;
  box-shadow: 1px 1px 2px #666;
  -webkit-box-shadow: 1px 1px 2px #666;
  -moz-box-shadow: 1px 1px 2px #666;
  width:250px;
  height:250px;
  display:block;
  -webkit-transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
   -o-transition: all .3s ease-in-out;
   -transition: all .3s ease-in-out;
    
}
/* first image animation properties */
.album:hover .photo1, .album:focus .photo1 {
    -webkit-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
    -moz-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
    -o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
    -ms-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
}

/* second image animation properties */
.album:hover .photo2, .album:focus .photo2 {
    -webkit-transform: rotate(0deg) translate(0, -3px) scale(1.1);
    -moz-transform: rotate(0deg) translate(0, -3px) scale(1.1);
    -o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
    -ms-transform: rotate(0deg) translate(0, -3px) scale(1.1);
}

/* third image animation properties */
.album:hover .photo3, .album:focus .photo3 {
    -webkit-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
    -moz-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
    -o-transform: rotate(-6deg) translate(-100px, -3px) scale(1.1);
    -ms-transform: rotate(6deg) translate(100px, -3px) scale(1.1);
}
</pre><p>Thanks to <a
href="http://twitter.com/ActualGabe">@ActualGabe</a> for pointing this out!</p><h2>The MooTools&nbsp;Version</h2><p>Since there was no method for animating each image to its original position with just CSS, and considering MooTools has the smoothest animations on the web, it was only natural that I use MooTools to complete the effect.  JavaScript is also nice in that we can do variable animations based on developer configuration and variable numbers of images.</p><p>After creating an initial inline-script, I refactored what I had to provide the following MooTools JavaScript class:</p><pre class="js">
var PhotoStack = new Class({
	
	Implements: [Options],
	
	options: {
		images: "img", // Which images inside the wrapper should we grab?
		rotationMax: 6, // Rotation max (both positive and negative)
		translationChange: 100, // Positive and negative,
		translationPxChange: 3, // Only positive
		scaleMax: 1.1, // Only positive, obviously,
		duration: 100 // Animation duration
	},
	
	initialize: function(wrapper, options) {
		this.setOptions(options);
		
		// Sort out elements
		this.wrapper = wrapper;
		this.images = [];
		
		// Add images
		wrapper.getElements(this.options.images).each(this.addImage, this);
		this.initialAdded = true;
		this.calculateSteps();
		
		// Add events
		this.addEvents();
		
		// This string will hold the proper calculation
		this.calculatedStyle = "";
	},
	
	calculateSteps: function() {
		// Get the images and calculation transformation values based on those images
		var images = this.images,
			numImages = images.length,
			halfImages = (numImages / 2),
			options = this.options;
		
		// Calculate the fx properties
		this.rotationIncrement = (options.rotationMax * 2 / (numImages - 1));
		this.rotationStart = options.rotationMax * -1;
		this.translationIncrement = options.translationChange / (numImages - 1);
		this.translationStart = options.translationChange * -1;
		this.translationPx = options.translationPxChange * -1;
	},
	
	addImage: function(image) {
		this.images.push(image);
		if(this.initialAdded) this.calculateSteps();
	},
	
	createFx: function(image) {
		if(image.retrieve("photostack")) return;
		
		// Create an instance of select
		var fx = new Fx({ duration: this.options.duration });
		fx.set = function(value) {
			
			// Calculate image settings specific to this instance
			var index = image.retrieve("photostack-index"),
				targetRotation = (this.rotationStart + (index * this.rotationIncrement)), // deg
				targetTranslation = (this.translationStart + (index * this.translationIncrement)), // px
				targetTranslationPx = this.translationPx; //px
			
			// Create the style string for this spot in the animation
			var style = "rotate(" + (targetRotation * value) + "deg) translate(" + (targetTranslation * value) + "px, " + (targetTranslationPx * value) + "px) scale(" + (1 + (value * (this.options.scaleMax - 1))) + ")";
			
			// Update those styles accordingly
			image.setStyles({
				"-webkit-transform": style,
				"-moz-transform": style,
				"-o-transform": style,
				"-ms-transform": style,
				transform: style
			});
		}.bind(this);
		
		// Store the fx object
		image.store("photostack", fx);
	},
	
	addEvents: function() {
		var images = this.images, wrapper = this.wrapper;
		
		// Create an event to lazyload photodeck fx creation
		var lazyFxEvent = function() {
			images.each(this.createFx, this);
			wrapper.removeEvent("mouseenter", lazyFxEvent);
			wrapper.removeEvent("focus", lazyFxEvent);
		}.bind(this);
		
		// Add the proper events
		wrapper.addEvent("mouseenter", lazyFxEvent);
		wrapper.addEvent("focus", lazyFxEvent);
		
		// Create basic mouseenter/leave events
		var todo = function(images, to, from) {
			return function() {
				images.each(function(image, index) {
					image.store("photostack-index", index);
					image.retrieve("photostack").start(to, from);
				});
			};
		};
		
		// Add the mouseenter and leave events to the album wrapper
		wrapper.addEvents({
			mouseenter: todo(images, 0, 1),
			focus: todo(images, 0, 1),
			mouseleave: todo(images, 1, 0),
			blur: todo(images, 1, 0)
		});
	}
});
</pre><p>There can be a lot of math involved with allowing for a variable number of images, so to ease the burden of those calculations, I&#8217;ve placed &#8220;max&#8221; options within the class to manage those calculations for the developer.  Using the class is as simple as:</p><pre class="js">
window.addEvent("domready", function() {
	$$(".album2").each(function(album) {
		new PhotoStack(album);
	});
});
</pre><p>The animation back to original state is a nice change from the abrupt state restoration from the CSS-only solution.  Feel free to take and update the class however you&#8217;d like!</p><div
class="actions"><a
href="http://davidwalsh.name/dw-content/photo-stack.php" class="demo">View Demo</a><div
class="clear"></div></div><p>Duplicating Google&#8217;s photo stack effect was much easier than I had expected it to be thanks to MooTools&#8217; flexible, easy to use Fx class.  If you aren&#8217;t worried about animating back to original position, you can stick to the simple CSS solution.  You can&#8217;t lose with either effect;  they&#8217;re both very smooth and add a subtle touch of class to an otherwise dull photo display.</p><p><em>If you&#8217;re looking for a jQuery alternative, <a
href="http://youhack.me/2011/07/15/google-plus-photo-stack-animation-using-jquery-and-css3/" rel="nofollow">this method</a> appears to be close to mine.</em></p><p><a
href="http://davidwalsh.name/photo-stack">Create a Photo Stack Effect with Pure CSS Animations or&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/photo-stack/feed</wfw:commentRss> <slash:comments>19</slash:comments> </item> <item><title>Add a Google+1 Badge to Your&#160;Website</title><link>http://davidwalsh.name/google-plus-badge</link> <comments>http://davidwalsh.name/google-plus-badge#comments</comments> <pubDate>Thu, 30 Jun 2011 22:47:17 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[APIs]]></category> <category><![CDATA[Google]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Markup]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5261</guid> <description><![CDATA[Google&#8217;s new Google+ service is taking the development world by storm, and why shouldn&#8217;t it?  It&#8217;s minimalistic UI, use of cutting edge web techniques, and overall usability make it Google+ one of the more impressive web applications I&#8217;ve seen in quite a while (I was going to say &#8220;since Google Wave&#8221;, but that one didn&#8217;t [...]<p><a
href="http://davidwalsh.name/google-plus-badge">Add a Google+1 Badge to Your&nbsp;Website</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/plusOneLogo.png" alt="Google+ Badge" class="image" /><p>Google&#8217;s new Google+ service is taking the development world by storm, and why shouldn&#8217;t it?  It&#8217;s minimalistic UI, use of cutting edge web techniques, and overall usability make it Google+ one of the more impressive web applications I&#8217;ve seen in quite a while <em>(I was going to say &#8220;since Google Wave&#8221;, but that one didn&#8217;t end up well, did it?)</em> Google+ allows you to share photos, links, statuses, and much more;  consider it a mesh between Twitter and Facebook.</p><p>Google+ will only gain more traction, meaning that the information we share will be pushed to more and more people.  This presents an excellent opportunity to promote our websites, much in the way that Twitter and Facebook have.  Let me show you a few different ways to add the new Google+1 badge to your website.</p><h2>The Simple&nbsp;HTML</h2><p>Adding a Google+1 badge is as easy as adding a few HTML tags:</p><pre class="html">
&lt;!-- one button, uses canonical link or current URL --&gt;
&lt;g:plusone&gt;&lt;/g:plusone&gt;

&lt;!-- customized for a specific address --&gt;
&lt;g:plusone href="http://davidwalsh.name/google-plus-badge"&gt;&lt;/g:plusone&gt;

&lt;!-- include the JS file --&gt;
&lt;script src="https://apis.google.com/js/plusone.js"&gt;&lt;/script&gt;
</pre><p>Provide the <code>g:plusone</code> tag the URL to share via the <code>HREF</code> attribute <em>(if none is provided, the page&#8217;s <a
href="http://davidwalsh.name/canonical-link-rel">canonical link</a> will be searched for and used, otherwise the current URL is used)</em> and the desired size of button via the size attribute.  This tag should be added wherever within the page that you&#8217;d like the button to appear;  the SCRIPT tag should be found at the bottom of the page.  If you&#8217;d prefer not to dabble with the HTML yourself, Google provides a <a
href="http://www.google.com/intl/en/webmasters/+1/button/index.html">Google+1 button builder</a> to do the dirty work for you.  Four button sizes are currently offered.</p><p>You could also use the following HTML5 code to render your button:</p><pre class="html">
&lt;div class="g-plusone" data-size="standard" data-count="true"&gt;&lt;/div&gt;
</pre><p>Your button must have the <code>g-plusone</code> CSS class and <code>data-</code> attributes for options.</p><h2>Deferring&nbsp;Loading</h2><p>The HTML code above will render with the rest of the page, but what if you want to render a button on your own schedule?  No worries &#8212; Google&#8217;s API allows you to render buttons whenever you&#8217;d like:</p><pre class="html">
&lt;!-- one button, uses canonical link or current URL --&gt;
&lt;g:plusone&gt;&lt;/g:plusone&gt;

&lt;!-- Place this tag in your head or just before your close body tag --&gt;
&lt;script src="https://apis.google.com/js/plusone.js"&gt;
  {parsetags: 'explicit'}
&lt;/script&gt;

&lt;!-- Now render! --&gt;
&lt;script&gt;
	gapi.plusone.go();
&lt;/script&gt;
</pre><p>Using <code>{parsetags: 'explicit'}</code> and <code>gapi.plusone.go()</code> will turn your <code>g:plusone</code> tags into badges whenever you&#8217;d like.  You can also render a specific button with the following JavaScript code:</p><pre class="js">
&lt;script type="text/javascript" src="https://apis.google.com/js/plusone.js"&gt;
	{"parsetags": "explicit"}
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
	function renderPlusone() {
		gapi.plusone.render("plusone-div");
	}
&lt;/script&gt;
&lt;body&gt;
	&lt;a href="#" onClick="renderPlusone();"&gt;Render the +1 button&lt;/a&gt;
	&lt;div id="plusone-div"&gt;&lt;/div&gt;
&lt;/body&gt;
</pre><p>Full API information as well as an introductory video is <a
href="http://code.google.com/apis/+1button/">available here</a>.  There are a few more small customization options available so check them out if you&#8217;re looking to specify language or play with custom rendering.</p><p>Don&#8217;t miss out on another avenue for sharing your awesome web content with everyone!  Implementing Twitter, Facebook, and Google+1 sharing buttons takes 5 minutes but the amount of traffic you can gain via these referral avenues is tremendous!</p><p><a
href="http://davidwalsh.name/google-plus-badge">Add a Google+1 Badge to Your&nbsp;Website</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/google-plus-badge/feed</wfw:commentRss> <slash:comments>26</slash:comments> </item> <item><title>Parse Web Pages with PHP Simple HTML DOM&#160;Parser</title><link>http://davidwalsh.name/php-notifications</link> <comments>http://davidwalsh.name/php-notifications#comments</comments> <pubDate>Wed, 15 Jun 2011 14:00:28 +0000</pubDate> <dc:creator>David Walsh</dc:creator> <category><![CDATA[Markup]]></category> <category><![CDATA[PHP]]></category><guid
isPermaLink="false">http://davidwalsh.name/?p=5244</guid> <description><![CDATA[For those of you who have had the pleasure of following me on Twitter (&#8230;), you probably know that I&#8217;m a complete soccer (football) fanatic.  I even started a separate Twitter account to voice my footy musings.  If you follow football yourself, you&#8217;ll know that we&#8217;ve just started the international transfer window and there are [...]<p><a
href="http://davidwalsh.name/php-notifications">Parse Web Pages with PHP Simple HTML DOM&nbsp;Parser</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/gervinho.jpg" alt="Gervinho to Arsenal" class="image" /><p>For those of you who have had the pleasure of following <a
href="http://twitter.com/davidwalshblog">me on Twitter</a> <em>(&#8230;)</em>, you probably know that I&#8217;m a complete soccer <em>(football)</em> fanatic.  I even started a <a
href="http://twitter.com/davidwalshfooty">separate Twitter account</a> to voice my footy musings.  If you follow football yourself, you&#8217;ll know that we&#8217;ve just started the international transfer window and there are a billion rumors about a billion players going to a billion clubs.  It&#8217;s enough to drive you mad but I simply <em>HAVE TO KNOW</em> who will be in the Arsenal and Liverpool first teams next season.</p><p>The problem I run into, besides all of the rubbish reports making waved, is that I don&#8217;t have time to check every website on the hour.  Twitter is a big help, but there&#8217;s nothing better during this time than an official report from each club&#8217;s website.  To keep an eye on those reports, I&#8217;m using the power of <a
href="http://simplehtmldom.sourceforge.net/">PHP Simple HTML DOM Parser</a> to write a tiny PHP script that shoots me an email whenever a specific page is updated.</p><h2>PHP Simple HTML DOM&nbsp;Parser</h2><p>PHP Simple HTML DOM Parser is a dream utility for developers that work with both PHP and the DOM because developers can easily find DOM elements using PHP.  Here are a few sample uses of PHP Simple HTML DOM Parser:</p><pre class="php">
// Include the library
include('simple_html_dom.php');
 
// Retrieve the DOM from a given URL
$html = file_get_html('http://davidwalsh.name/');

// Find all "A" tags and print their HREFs
foreach($html-&gt;find('a') as $e) 
    echo $e-&gt;href . '&lt;br&gt;';

// Retrieve all images and print their SRCs
foreach($html-&gt;find('img') as $e)
    echo $e-&gt;src . '&lt;br&gt;';

// Find all images, print their text with the "&lt;&gt;" included
foreach($html-&gt;find('img') as $e)
    echo $e-&gt;outertext . '&lt;br&gt;';

// Find the DIV tag with an id of "myId"
foreach($html-&gt;find('div#myId') as $e)
    echo $e-&gt;innertext . '&lt;br&gt;';

// Find all SPAN tags that have a class of "myClass"
foreach($html-&gt;find('span.myClass') as $e)
    echo $e-&gt;outertext . '&lt;br&gt;';

// Find all TD tags with "align=center"
foreach($html-&gt;find('td[align=center]') as $e)
    echo $e-&gt;innertext . '&lt;br&gt;';
    
// Extract all text from a given cell
echo $html-&gt;find('td[align="center"]', 1)-&gt;plaintext.'&lt;br&gt;&lt;hr&gt;';
</pre><p>Like I said earlier, this library is a dream for finding elements, just as the early JavaScript frameworks and selector engines have become.  Armed with the ability to pick content from DOM nodes with PHP, it&#8217;s time to analyze websites for changes.</p><h2>The&nbsp;Script</h2><p>The following script checks two websites for changes:</p><pre class="php">
// Pull in PHP Simple HTML DOM Parser
include("simplehtmldom/simple_html_dom.php");

// Settings on top
$sitesToCheck = array(
					// id is the page ID for selector
					array("url" =&gt; "http://www.arsenal.com/first-team/players", "selector" =&gt; "#squad"),
					array("url" =&gt; "http://www.liverpoolfc.tv/news", "selector" =&gt; "ul[style='height:400px;']")
				);
$savePath = "cachedPages/";
$emailContent = "";

// For every page to check...
foreach($sitesToCheck as $site) {
	$url = $site["url"];
	
	// Calculate the cachedPage name, set oldContent = "";
	$fileName = md5($url);
	$oldContent = "";
	
	// Get the URL's current page content
	$html = file_get_html($url);
	
	// Find content by querying with a selector, just like a selector engine!
	foreach($html-&gt;find($site["selector"]) as $element) {
		$currentContent = $element-&gt;plaintext;;
	}
	
	// If a cached file exists
	if(file_exists($savePath.$fileName)) {
		// Retrieve the old content
		$oldContent = file_get_contents($savePath.$fileName);
	}
	
	// If different, notify!
	if($oldContent &amp;&amp; $currentContent != $oldContent) {
		// Here's where we can do a whoooooooooooooole lotta stuff
		// We could tweet to an address
		// We can send a simple email
		// We can text ourselves
		
		// Build simple email content
		$emailContent = "David, the following page has changed!\n\n".$url."\n\n";
	}
	
	// Save new content
	file_put_contents($savePath.$fileName,$currentContent);
}

// Send the email if there's content!
if($emailContent) {
	// Sendmail!
	mail("&#100;&#97;&#118;&#105;d&#64;d&#97;&#118;&#105;&#100;w&#97;&#108;&#115;&#104;.&#110;a&#109;&#101;","Sites Have Changed!",$emailContent,"From: al&#101;&#114;&#116;&#115;&#64;da&#118;idwal&#115;&#104;&#46;&#110;a&#109;&#101;","\r\n");
	// Debug
	echo $emailContent;
}
</pre><p>The code and comments are self-explanatory.  I&#8217;ve set the script up such that I get one &#8220;digest&#8221; alert if many of the pages change.  The script is the hard part &#8212; to enact the script, I&#8217;ve set up a CRON job to run the script every 20 minutes.</p><p>This solution isn&#8217;t specific to just spying on footy &#8212; you could use this type of script on any number of sites.  This script, however, is a bit simplistic in all cases.  If you wanted to spy on a website that had extremely dynamic code (i.e. a timestamp was in the code), you would want to create a regular expressions that would isolate the content to just the block you&#8217;re looking for.  Since each website is constructed differently, I&#8217;ll leave it up to you to create page-specific isolators.  Have fun spying on websites though&#8230;and be sure to let me know if you hear a good, reliable footy rumor!</p><p><a
href="http://davidwalsh.name/php-notifications">Parse Web Pages with PHP Simple HTML DOM&nbsp;Parser</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/php-notifications/feed</wfw:commentRss> <slash:comments>21</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 1/63 queries in 0.042 seconds using disk: basic
Object Caching 1443/1544 objects using disk: basic

Served from: davidwalsh.name @ 2012-05-23 22:41:26 -->
