<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.ravelrumba.com/~d/styles/itemcontent.css"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Ravelrumba by Rob Flaherty</title>
	
	<link>http://www.ravelrumba.com</link>
	<description>web design &amp; development</description>
	<lastBuildDate>Mon, 08 Apr 2013 13:37:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.ravelrumba.com/ravelrumba" /><feedburner:info uri="ravelrumba" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Asynchronous Ad Loading With Iframes</title>
		<link>http://feeds.ravelrumba.com/~r/ravelrumba/~3/w_Jj3ZeeIyA/</link>
		<comments>http://www.ravelrumba.com/blog/asynchronous-ad-loading-iframes/#comments</comments>
		<pubDate>Thu, 29 Nov 2012 18:23:28 +0000</pubDate>
		<dc:creator>Rob Flaherty</dc:creator>
				<category><![CDATA[General Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Publishing]]></category>
		<category><![CDATA[Web Performance]]></category>
		<category><![CDATA[ad server]]></category>
		<category><![CDATA[ads]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[iframes]]></category>

		<guid isPermaLink="false">http://www.ravelrumba.com/?p=1865</guid>
		<description><![CDATA[I&#8217;ve written a few posts about working with ad servers (see here and here) and in both, the discussion in the comments sections steered into questions about ad performance. There are numerous performance issues related to ad loading but the main issue is the blocking effect caused by third-party network requests implemented with document.write(). Here&#8217;s [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve written a few posts about working with ad servers (see <a href="/blog/cleaning-up-ad-server-scripts/">here</a> and <a href="/blog/responsive-ads-real-world-ad-server-implementation/">here</a>) and in both, the discussion in the comments sections steered into questions about ad performance. There are numerous performance issues related to ad loading but the main issue is the blocking effect caused by third-party network requests implemented with <code>document.write()</code>. Here&#8217;s a demo showing a <a href="http://www.ravelrumba.com/code/demos/blocking-ads/demo1.html" target="_blank">request with a 3-second delay blocking the page content</a>.</p>
<p>One way around this is to sandbox ads within iframes to allow them to load independently of the page content. <a href="http://www.ravelrumba.com/code/demos/blocking-ads/demo3.html" target="_blank">Here&#8217;s another demo</a> showing the same 3-second lagging ad request but this time the ad is load within an iframe, allowing the content to display unblocked.</p>
<div class="post-demo-links">
<h2>Demos featured in this post:</h2>
<ul>
<li><a href="http://www.ravelrumba.com/code/demos/blocking-ads/demo1.html" target="_blank">Example showing content blocked by delayed ad request</a></li>
<li><a href="http://www.ravelrumba.com/code/demos/blocking-ads/demo3.html" target="_blank">Example showing iframe ad loading resolving blocking problem</a></li>
<li><a href="http://www.ravelrumba.com/code/demos/iframe-ads/noiframes.html" target="_blank">Example showing mock OAS implementation with blocking ads</a></li>
<li><a href="http://www.ravelrumba.com/code/demos/iframe-ads/iframes.html" target="_blank">Example showing mock OAS implementation with iframes resolving blocking problem</a></li>
</ul>
</div>
<p>Loading ads in iframes is nothing new, and in some cases it&#8217;s easy to do; but if you&#8217;re working with many ads per page or uncooperative ad servers, it can be more of a challenge. For instance, when there are multiple ads on a page it&#8217;s common for the ad server to have to coordinate which ads display at the same time. There may be a requirement that Advertiser A&#8217;s campaigns do not display at the same time as Advertiser B&#8217;s. Or there may be companion units that have to display at the same time. This inter-ad-unit awareness is often achieved by batching the ad requests into a single call to the ad server. But, depending on your ad server, this technique may not be compatible with iframe ad loading.</p>
<p>In this post I&#8217;ll show an example of how you can hack one ad server (24/7 Open AdStream) to make its single-request implementation work with iframes.</p>
<h5>MJX</h5>
<p>OAS has several different implementation methods (NX, RX, JX, SX, MJX), each with pros and cons. The one I&#8217;ve used the most is MJX, which allows for the greatest control over how campaigns can be coordinated with one another (companion serving, competitive serving, etc) by batching the individual ad requests into a single ad server request. This is how it works.</p>
<p>In the HEAD section of your page you build up a request to OAS that tells the ad server which ad positions you want, what &#8220;page&#8221; (&#8220;Page Tag&#8221; in OAS terms) the campaigns should correspond to, and possibly some other stuff like key/value pairs to achieve additional targeting layers. OAS then returns a script with a function that writes out each ad unit. It looks something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> OAS_RICH<span style="color: #009900;">&#40;</span>position<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>position <span style="color: #339933;">==</span> <span style="color: #3366CC;">'Top'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    document.<span style="color: #000066; font-weight: bold;">write</span><span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>position <span style="color: #339933;">==</span> <span style="color: #3366CC;">'Right'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    document.<span style="color: #000066; font-weight: bold;">write</span><span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>This function is then invoked with the corresponding position name from the various ad placements on the page (e.g., <code>&lt;script&gt;OAS_RICH('Top')&lt;/script&gt;</code>).</p>
<p><a href="http://www.ravelrumba.com/code/demos/iframe-ads/noiframes.html" target="_blank">Here&#8217;s an example showing a mock MJX implementation</a> that includes 2 ads, each consisting of JS that uses document.write to write an image to the page. The first script is returned with a 2-second delay, the second script with a 3-second delay. You can see how both paragraphs of text are blocked while we wait for the scripts to return from the server.</p>
<p>Notice that <strong>everything</strong> is blocked, including the request for the second ad, which means the delays are compounded, which means it takes 5+ seconds for both the DomContentLoaded and window.onload events to fire.</p>
<h5>Iframes</h5>
<p>So at this point we have a global function, <code>OAS_RICH()</code>, that&#8217;s responsible for writing each ad unit. To load the ads within iframes we need to create documents for each ad position and then call the <code>OAS_RICH()</code> function from within the child documents.</p>
<p>This is fairly easy but it&#8217;s not quite as easy as just calling <code>parent.OAS_RICH('Top')</code> from the iframed window. Why not? Because of JavaScript&#8217;s static/lexical scoping. The document object defined in the OAS_RICH function refers to the <strong>parent window document</strong>, not the child window document invoking the function. So calling the function from within the iframe overwrites the parent document. That&#8217;s not what we want.</p>
<p>I don&#8217;t know of a solution to this that isn&#8217;t a hack. But maybe someone with more experience working with iframes can offer a better idea.</p>
<p>The easiest solution, I think, is to convert the OAS_RICH function to a string and then reevaluate the function in the context of the iframe.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> ad <span style="color: #339933;">=</span> parent.<span style="color: #660066;">OAS_RICH</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000066; font-weight: bold;">eval</span><span style="color: #009900;">&#40;</span>ad<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
OAS_RICH<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'Top'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>This is a little funky because aside from the <code>eval</code> usage this reevaluation has to be done for each ad unit on the page.</p>
<p>Another option: In the parent document convert the function to a string, regex replace each &#8220;document&#8221; with &#8220;this.document&#8221;, and then <code>eval</code> the string to redefine the function. Then you can call the function using <code>apply()</code> within the child document and pass it the child document context. The advantage here is that the funky function->string->function action only happens once. The disadvantage is that you&#8217;re introducing a regex and manipulating the contents of the function.</p>
<p><a href="http://www.ravelrumba.com/code/demos/iframe-ads/iframes.html" target="_blank">Here&#8217;s an example</a> showing how the content isn&#8217;t blocked when the iframe implementation is used. The ads load independently, the DomContentLoaded event fires right away, and the window.onload event occurs after 3 seconds instead of 5.</p>
<p>(As a side note, I should point out that serving expanding ad units with iframes can require extra steps to allow the creative to &#8220;break out&#8221; of the iframe.)</p>
<h5>Get to know your 3rd party code</h5>
<p>This is kind of hacky but it&#8217;s also not very complicated, so I think it could be a useable solution (disclaimer: I haven&#8217;t used this in Production).</p>
<p>However the point of the post wasn&#8217;t to deliver ready-to-paste code, but to crack open some third-party JavaScript to understand how it works and how we might bend/extend it to meet our needs. Of course you have to be very careful doing this—don&#8217;t just run around tearing up vendor code. But there&#8217;s a lot you can learn by reaching in, getting your hands greasy, and making 3rd-party code your own.</p>
<img src="http://feeds.feedburner.com/~r/ravelrumba/~4/w_Jj3ZeeIyA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ravelrumba.com/blog/asynchronous-ad-loading-iframes/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ravelrumba.com/blog/asynchronous-ad-loading-iframes/</feedburner:origLink></item>
		<item>
		<title>Tracking iOS Web App Usage</title>
		<link>http://feeds.ravelrumba.com/~r/ravelrumba/~3/MPb2x0eDznA/</link>
		<comments>http://www.ravelrumba.com/blog/tracking-ios-web-app-usage/#comments</comments>
		<pubDate>Wed, 03 Oct 2012 17:38:18 +0000</pubDate>
		<dc:creator>Rob Flaherty</dc:creator>
				<category><![CDATA[General Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[analytics]]></category>
		<category><![CDATA[google analytics]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[web app]]></category>

		<guid isPermaLink="false">http://www.ravelrumba.com/?p=1843</guid>
		<description><![CDATA[Via a handful of meta tags and link tags, web apps on iOS can be launched from the home screen and run in fullscreen mode to enable a more native-like experience for the user. Turning your website or app into a &#8220;home screen web app&#8221; is not difficult but it does require a chunk of [...]]]></description>
				<content:encoded><![CDATA[<p>Via a handful of meta tags and link tags, web apps on iOS can be launched from the home screen and run in fullscreen mode to enable a more native-like experience for the user. Turning your website or app into a &#8220;home screen web app&#8221; is not difficult but it does require <a href="http://blog.cloudfour.com/seven-things-i-learned-while-making-an-ipad-web-app-2/" target="_blank">a chunk of work</a> including designing a home screen icon, designing a startup screen image, preventing links from opening in Mobile Safari (and exiting the app), and prompting the user to add the app to their home screen. If we&#8217;re going to go through the trouble wouldn&#8217;t it be nice to have some data on how many people are actually using the thing?</p>
<p>Fortunately this is easy to do. Of course you can track visits and pageviews with the standard Google Analytics implementation, but there are some app-specific behaviors we can track with just a little bit of JavaScript.</p>
<p>So I&#8217;m going to show some examples of how to track home screen web app activity using Google Analytics custom variables. Depending on what exactly you want to track you may consider using Google Analytics events instead, but for data directly tied to pageviews or visits I think custom variables is the cleaner choice.</p>
<p>Remember that custom variables definitions must be followed by a _trackPageview() or _trackEvent() request. So as far as code placement is concerned you would place the following examples after the initial Google Analytics set up snippet and before the _trackPageview() function call. Also note that each of the examples uses the same custom variable slot. You&#8217;ll want to replace these with your own slot values.</p>
<h5>Track home screen launches</h5>
<p>This sets a custom variable every time we detect a visit that occurs in the web app mode. This will allow you to segment the home screen web app traffic and compare it against traffic arriving through the browser.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>navigator.<span style="color: #660066;">standalone</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
  _gaq.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'_setCustomVar'</span><span style="color: #339933;">,</span>
    <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span>                       <span style="color: #006600; font-style: italic;">// Which custom variable slot to use</span>
    <span style="color: #3366CC;">'Mobile Web App'</span><span style="color: #339933;">,</span>        <span style="color: #006600; font-style: italic;">// Custom variable name</span>
    <span style="color: #3366CC;">'Home Screen Launch'</span><span style="color: #339933;">,</span>    <span style="color: #006600; font-style: italic;">// Custom variable value</span>
    <span style="color: #CC0000;">2</span>                        <span style="color: #006600; font-style: italic;">// Sets the scope to session-level</span>
  <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<h5>Track device orientation</h5>
<p>This keeps track of whether the app is being viewed in portrait or landscape mode. Note that in this example the custom variable is defined at the page level, not the session level as in the previous example.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>navigator.<span style="color: #660066;">standalone</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
  <span style="color: #003366; font-weight: bold;">var</span> currentOrientation <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>window.<span style="color: #660066;">innerHeight</span> <span style="color: #339933;">&gt;</span> window.<span style="color: #660066;">innerWidth</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #3366CC;">'Portrait'</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'Landscape'</span><span style="color: #339933;">;</span>
&nbsp;
  _gaq.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'_setCustomVar'</span><span style="color: #339933;">,</span>
    <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span>                              <span style="color: #006600; font-style: italic;">// Which custom variable slot to use</span>
    <span style="color: #3366CC;">'Mobile Web App Orientation'</span><span style="color: #339933;">,</span>   <span style="color: #006600; font-style: italic;">// Custom variable name</span>
    currentOrientation<span style="color: #339933;">,</span>             <span style="color: #006600; font-style: italic;">// 'Portrait' or 'Landscape'</span>
    <span style="color: #CC0000;">3</span>                               <span style="color: #006600; font-style: italic;">// Sets the scope to page-level</span>
  <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<h5>Track time since last launch</h5>
<p>This keeps track of how many days have passed since the last time the app was opened by logging a timestamp in local storage and comparing the dates whenever the app is launched. There might be a way to get at this same data just by looking at returning visitor stats in the standard Google Analytics view. Not sure though. And doing it with JavaScript is more fun.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>navigator.<span style="color: #660066;">standalone</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
  <span style="color: #006600; font-style: italic;">// Local storage detection</span>
  <span style="color: #003366; font-weight: bold;">var</span> storage <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> uid <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Date<span style="color: #339933;">,</span>
      storage<span style="color: #339933;">,</span>
      result<span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #009900;">&#40;</span>storage <span style="color: #339933;">=</span> window.<span style="color: #660066;">localStorage</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">setItem</span><span style="color: #009900;">&#40;</span>uid<span style="color: #339933;">,</span> uid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      result <span style="color: #339933;">=</span> storage.<span style="color: #660066;">getItem</span><span style="color: #009900;">&#40;</span>uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> uid<span style="color: #339933;">;</span>
      storage.<span style="color: #660066;">removeItem</span><span style="color: #009900;">&#40;</span>uid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000066; font-weight: bold;">return</span> result <span style="color: #339933;">&amp;&amp;</span> storage<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">catch</span><span style="color: #009900;">&#40;</span>e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #006600; font-style: italic;">// If device has support for local storage</span>
  <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>storage<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> lastLaunch<span style="color: #339933;">;</span> 
&nbsp;
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>storage.<span style="color: #660066;">webAppLastLaunch</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #006600; font-style: italic;">// If app is launched for the first time    </span>
      lastLaunch <span style="color: #339933;">=</span> <span style="color: #3366CC;">'First Launch'</span><span style="color: #339933;">;</span>      
&nbsp;
    <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #006600; font-style: italic;">// Calculate days since last recorded launch</span>
      lastLaunch <span style="color: #339933;">=</span>  Math.<span style="color: #660066;">floor</span><span style="color: #009900;">&#40;</span> <span style="color: #009900;">&#40;</span> <span style="color: #339933;">+</span><span style="color: #003366; font-weight: bold;">new</span> Date <span style="color: #339933;">-</span> storage.<span style="color: #660066;">webAppLastLaunch</span> <span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #CC0000;">86400000</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    storage.<span style="color: #660066;">setItem</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'webAppLastLaunch'</span><span style="color: #339933;">,</span> <span style="color: #339933;">+</span><span style="color: #003366; font-weight: bold;">new</span> Date<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    _gaq.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'_setCustomVar'</span><span style="color: #339933;">,</span>
      <span style="color: #CC0000;">1</span><span style="color: #339933;">,</span>                <span style="color: #006600; font-style: italic;">// Which custom variable slot to use</span>
      <span style="color: #3366CC;">'Last launch'</span><span style="color: #339933;">,</span>    <span style="color: #006600; font-style: italic;">// Custom variable name</span>
      lastLaunch<span style="color: #339933;">,</span>       <span style="color: #006600; font-style: italic;">// &quot;First Launch&quot; or the number of days since last launch</span>
      <span style="color: #CC0000;">2</span>                 <span style="color: #006600; font-style: italic;">// Sets the scope to session-level</span>
    <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<h5>What else?</h5>
<p>These are just the first few ideas that popped into my head. Is there anything else that would be interesting to track?</p>
<img src="http://feeds.feedburner.com/~r/ravelrumba/~4/MPb2x0eDznA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ravelrumba.com/blog/tracking-ios-web-app-usage/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ravelrumba.com/blog/tracking-ios-web-app-usage/</feedburner:origLink></item>
		<item>
		<title>Avoid unnecessary redirects on mobile sites</title>
		<link>http://feeds.ravelrumba.com/~r/ravelrumba/~3/F6SnQcFXdcU/</link>
		<comments>http://www.ravelrumba.com/blog/avoid-unnecessary-redirects-mobile-sites/#comments</comments>
		<pubDate>Wed, 20 Jun 2012 03:07:18 +0000</pubDate>
		<dc:creator>Rob Flaherty</dc:creator>
				<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Web Performance]]></category>
		<category><![CDATA[device-specific sites]]></category>

		<guid isPermaLink="false">http://www.ravelrumba.com/?p=1808</guid>
		<description><![CDATA[Avoiding unnecessary redirects is a general performance best practice but in some cases it can be easy for unwanted redirects to sneak into your application. The performance penalty rendered by unnecessary redirects is of particular importance for mobile users who may be dealing with high-latency, low-bandwidth connections. Here&#8217;s one scenario to watch out for when developing [...]]]></description>
				<content:encoded><![CDATA[<p>Avoiding unnecessary redirects is a general <a href="http://developer.yahoo.com/blogs/ydn/posts/2007/07/high_performanc_9/" target="_blank">performance best practice</a> but in some cases it can be easy for unwanted redirects to sneak into your application. The performance penalty rendered by unnecessary redirects is of particular importance for mobile users who may be dealing with high-latency, low-bandwidth connections.</p>
<p>Here&#8217;s one scenario to watch out for when developing mobile and other device-specific sites.</p>
<h5>One redirect</h5>
<p>If you&#8217;re redirecting a user based on the user&#8217;s device you want this redirect to happen <strong>only once</strong>. So if a user visits <em>www.yourdomain.com</em> and gets redirected to <em>mobile.yourdomain.com</em>, all links from that point on should be relative to the mobile subdomain. If you don&#8217;t do this, if your mobile site links back to the <em>www</em> subdomain, you tack on the wait time for the <em>www</em>-&gt;<em>mobile</em> redirect to each request. This is dumb.</p>
<p>You&#8217;ve probably configured your application or CMS or whatever to output relative URLs, so in most cases you should be OK (but you should check if you&#8217;re not sure.) But if your site uses a CMS where users routinely link to other pages—for instance a news site where editors link to other articles—it&#8217;s likely that these links contain absolute URLs referring to the desktop site.</p>
<p>That is, a link within an article that references another article may point to <em>http://www.mydomain.com/article-2.html</em>, as opposed to <em>/article-2.html</em>. Following this link from the mobile site results in another redirect. I&#8217;ve seen this on a number of mobile sites. Take a look at <a href="http://m.bleacherreport.com" target="_blank">m.bleacherreport.com</a> or <a href="http://mobile.theverge.com/" target="_blank">mobile.theverge.com</a> for live examples.</p>
<h5>How to fix</h5>
<p>It&#8217;s an easy thing to fix. One option is to have your CMS filter out same-domain absolute URLs before writing to the database. This is a good solution but requires that you have URL path parity between your desktop and mobile sites.</p>
<p>Another option is to add JavaScript to the mobile site to rewrite the links. Something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// Rewrite article links to relative URLS</span>
$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#article-body a'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>index<span style="color: #339933;">,</span> elem<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #003366; font-weight: bold;">var</span> oldhref <span style="color: #339933;">=</span> elem.<span style="color: #660066;">href</span><span style="color: #339933;">,</span>
    newhref <span style="color: #339933;">=</span> oldhref.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'http://www.yourdomain.com'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  $<span style="color: #009900;">&#40;</span>elem<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">attr</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'href'</span><span style="color: #339933;">,</span> newhref<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>This requires the same sort of path parity but may be easier to implement. </p>
<h5>That&#8217;s it</h5>
<p>So the takeaway is to avoid cross-domain links that always redirect back to the originating domain. This can happen under various circumstances but a common one concerns mobile sites displaying content with hard-coded references to the desktop site. The solution is to ensure that all links are generated with relative URLs.</p>
<p>Of course you can avoid the problem entirely by not having separate device-specific sites, but that&#8217;s another matter!</p>
<img src="http://feeds.feedburner.com/~r/ravelrumba/~4/F6SnQcFXdcU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ravelrumba.com/blog/avoid-unnecessary-redirects-mobile-sites/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ravelrumba.com/blog/avoid-unnecessary-redirects-mobile-sites/</feedburner:origLink></item>
		<item>
		<title>Interaction design pattern for brand interaction</title>
		<link>http://feeds.ravelrumba.com/~r/ravelrumba/~3/9g01LynHk3c/</link>
		<comments>http://www.ravelrumba.com/blog/interaction-design-pattern-for-brand-interaction/#comments</comments>
		<pubDate>Sat, 02 Jun 2012 18:19:29 +0000</pubDate>
		<dc:creator>Rob Flaherty</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[UX]]></category>
		<category><![CDATA[brand interaction]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[design patterns]]></category>
		<category><![CDATA[interaction design]]></category>

		<guid isPermaLink="false">http://www.ravelrumba.com/?p=1779</guid>
		<description><![CDATA[Github does this clever thing where if you right-click on their logo, a modal window opens with options to download Github&#8217;s brand assets. Pretty slick. It anticipates a common action (right-clicking a logo to download it as an image) and it provides the most logical, most direct path possible for accessing the logo itself: clicking [...]]]></description>
				<content:encoded><![CDATA[<p>Github does this clever thing where if you right-click on their logo, a modal window opens with options to download Github&#8217;s brand assets.</p>
<p><img class="alignnone size-full wp-image-1781" src="http://s.ravelrumba.com/uploads/2012/06/github-logo-interaction.jpg" alt="Screenshot of Github showing options to download logo" width="600" height="342" /></p>
<p>Pretty slick. It anticipates a common action (right-clicking a logo to download it as an image) and it provides the most logical, most direct path possible for accessing the logo itself: clicking the logo itself.</p>
<p>I remembered this the other day when trying to decide how to organize navigational elements for a mobile design I was working on. I wanted to separate the principal navigation actions from the ancillary actions, but I was struggling to find an elegant way to distinguish the two. I noticed that most of the ancillary actions had to do with what I thought of as brand interaction, which is to say a <strong>sort of meta activity focused on connecting with the brand</strong> as opposed to using the app.</p>
<p>Hmmm. So what&#8217;s the most obvious access point for interacting with the brand?</p>
<h5>Simple idea for a pattern</h5>
<p>Imagine this is the navigation bar for your app or mobile site:</p>
<p><img class="alignnone size-full wp-image-1784" title="brand-interaction-closed" src="http://s.ravelrumba.com/uploads/2012/06/brand-interaction-closed.jpg" alt="Screenshot of mobile app navigation bar" width="600" height="121" /></p>
<p>There are only two navigation options: a home button and a menu button that exposes a drop-down, or slide-out, or modal, etc, some form of navigation menu.</p>
<p>Of course at this point your eyes have raced ahead and you know that I&#8217;m lying because there are actually at least three navigation options. The logo can provide access to site actions as well:</p>
<p><img class="alignnone size-full wp-image-1785" title="brand-interaction-open" src="http://s.ravelrumba.com/uploads/2012/06/brand-interaction-open.jpg" alt="Screenshot of mobile app navigation bar" width="600" height="232" /></p>
<p>The behavior suggested here is that tapping on the logo reveals a tray-like set of options for things like social media connection, RSS, email sign-up or contact, and app/site settings. These are all things that group together fairly well and all things that fit nicely under the &#8220;brand&#8221; label. (One exception might be the settings button, whose place in the tray is earned more for its meta-ness than brand relation.)</p>
<p>I like that this solution achieves the desired separation of core and auxiliary actions but what I really like is how it gives the auxiliary actions <strong>a logical home</strong>. Granted it&#8217;s not an explicit navigation option (I can hear the sirens of the Usability Police getting nearer as I type). But it&#8217;s discoverable and something I think could become quite predictable were this design pattern to become a thing.</p>
<h5>Wait, but&#8230;</h5>
<p>I know, website logos already perform an interaction function. They double as a homepage link. Sometimes, as is the case with this blog, there is no explicit home navigation and the logo IS the home link. It&#8217;s convenient for both site authors and users. The user expects it. It&#8217;s a best practice.</p>
<p>That&#8217;s all true. But I think it&#8217;s a heuristic that developed in the absence of a better alternative. Might as well the link the logo back to the homepage if you&#8217;re not going to do anything else with it. While I&#8217;m not arguing against home-linking logos, I don&#8217;t think we should write it off as a reserved behavior.</p>
<p>Deviating from this practice obviously raises the possibility of friction but I see it as low-cost. The user gets immediate feedback and the substituted behavior is innocuous. Also, in a mobile or app environment, where this would seem to have the most potential, the notion of a home-linked logo probably isn&#8217;t as relevant as on a desktop site.</p>
<p>If you know of any sites or apps doing this (I&#8217;m sure there are more than a few) please leave a note in the comments so the curious of us can have a look.</p>
<img src="http://feeds.feedburner.com/~r/ravelrumba/~4/9g01LynHk3c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ravelrumba.com/blog/interaction-design-pattern-for-brand-interaction/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://www.ravelrumba.com/blog/interaction-design-pattern-for-brand-interaction/</feedburner:origLink></item>
		<item>
		<title>Squander</title>
		<link>http://feeds.ravelrumba.com/~r/ravelrumba/~3/QdsG0J2hfDg/</link>
		<comments>http://www.ravelrumba.com/blog/squander/#comments</comments>
		<pubDate>Sat, 26 May 2012 18:46:20 +0000</pubDate>
		<dc:creator>Rob Flaherty</dc:creator>
				<category><![CDATA[eMedia]]></category>
		<category><![CDATA[General Web]]></category>
		<category><![CDATA[Publishing]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[media]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[publishing]]></category>
		<category><![CDATA[social media]]></category>

		<guid isPermaLink="false">http://www.ravelrumba.com/?p=1677</guid>
		<description><![CDATA[A few weeks ago Aaron Gustafson wrote a post explaining how third-party widgets can compromise user privacy by tracking users&#8217; browsing activity. It&#8217;s something many developers are familiar with but Aaron took the additional step of detailing a roll-your-own solution for popular third-party widgets. It&#8217;s good. If you make websites you should check it out. The [...]]]></description>
				<content:encoded><![CDATA[<p>A few weeks ago Aaron Gustafson wrote a post explaining how <a href="http://blog.easy-designs.net/archives/2012/04/24/dont-sell-out-your-users/" target="_blank">third-party widgets can compromise user privacy</a> by tracking users&#8217; browsing activity. It&#8217;s something many developers are familiar with but Aaron took the additional step of detailing a roll-your-own solution for popular third-party widgets. It&#8217;s good. If you make websites you should check it out.</p>
<p>The shadowy transmission of user data that Aaron mentions raises concerns for multiple reasons. For one, end users have an interest in protecting their own privacy. Two, people who make websites have an obligation to protect user privacy for the benefit of their users (to what degree they are obliged is of course the subject of <a href="http://www.guardian.co.uk/technology/2012/may/26/cookies-law-changed-implied-consent" target="_blank">much debate</a>).</p>
<p>There&#8217;s a third case that gets less attention, and it has to do with the interest that audience-monetized websites have in protecting user data <em>for their own benefit.</em> And I don&#8217;t mean it in the sense that honorable websites that look out for their users will be rewarded by appreciative customers. I&#8217;m referring to the importance of not letting valuable audience data escape to your competitors&#8217; servers. That&#8217;s what this post is about.</p>
<p>First, I&#8217;ll restate the problem and concerned parties so you can decide if this is something you&#8217;re interested in.</p>
<p><strong>Problem:</strong> Adding third-party widgets like Facebook&#8217;s Like button and Twitter&#8217;s Tweet button to your site opens up a one-way channel of data transmission between your site and the widget provider. This data allows the widget provider to know which pages on your site their users view.</p>
<p><strong>Who should care about this:</strong> In the broadest sense, anyone who works on digital products. In particular, digital products with revenue streams dependent on providing third parties access to audiences. More particularly, products that generate revenue from advertising. Most particularly, the publishing industry, who, having spent the better part of the last decade hoping the internet would go away, is most vulnerable as it tries to wobble up off the mat.</p>
<p>What&#8217;s the big deal if Facebook knows that one of their users viewed certain pages on your site? Let&#8217;s get into it.</p>
<h5>Free content cannot be monetized</h5>
<p>First off let&#8217;s agree that free content is never monetized. Content you give away for free has no exchange value. That&#8217;s why it&#8217;s free. You can, of course, make lots of money offering free content by monetizing the audience that consumes your content.</p>
<p>Niche media markets provide an easy way to illustrate how this works. Imagine you&#8217;re in the business of selling vintage gumball machines to gumball machine enthusiasts. How do you get exposure to the small group of people interested in your product?</p>
<p>Ten years ago you would buy an ad in a print publication aimed at gumball machine enthusiasts. The publication would contain all sorts of interesting content for people who love antique gumball machines but the person paying for this whole operation—you, the advertiser—would obviously not be paying for the content but for access to a particular audience. And access to that audience would be worth a lot because how the hell else are you going to find those people?</p>
<p>For the publisher this is all good until someone else slithers into the market with a competing publication and offers access to the same audience for less.</p>
<p>OK, so this is Publishing 101, Advertising 101, nothing new, you already know this, everyone who works in the industry already knows it. And yet&#8230;</p>
<h5>Who are we competing with?</h5>
<p>Last year a financial institution invited a handful of media companies to their offices to talk about an upcoming rebranding campaign. I was in attendance and the list of invitees was made up of standard players like The Economist, Dow Jones, and Time. But there was one name that stood out from the others: LinkedIn.</p>
<p>I was delighted because LinkedIn&#8217;s name on the schedule was proof of something that I and one of my tech colleagues had been trying to impress upon others for awhile—that social networks like LinkedIn and Facebook are not partners with media companies, but competitors.</p>
<p>At first the response from some of my colleagues was one of confusion. &#8220;LinkedIn? Why are they here?&#8221; What was surprising was how quickly the confusion was dismissed and the threat shrugged off. Here we were competing for the <em>same advertising budget on the same afternoon</em> and yet they could still not see a social media company as a competitor. Now I was confused.</p>
<p>After a little discussion the answer emerged: &#8220;Yeah, but LinkedIn doesn&#8217;t write news.&#8221; Right. So as long as LinkedIn doesn&#8217;t start writing news we&#8217;re all good. Hmmm.</p>
<h5>Fuuuuuuungibility</h5>
<p>One of the very best pieces I&#8217;ve read on the state of online journalism is a recent article by Stijn Debrouwere titled <a href="http://stdout.be/2012/05/04/fungible/" target="_blank">Fungible</a>. If you&#8217;re unfamiliar with the term, a fungible object is one that can be exchanged for another object of identical substance and value.</p>
<p>The article&#8217;s thesis is that &#8220;&#8230;journalism is not being disrupted by better journalism but by things that are hardly recognizable as journalism at all.&#8221; Stijn writes about how sites like Quora, Wikipedia, IMDB, and EveryBlock now fulfill many of the needs that people used to turn to journalism for. Audiences are exchanging journalism for non-journalism. And the thing is that they&#8217;re not exchanging it for something else, but for something that amounts to the same thing. (That&#8217;s the fungible part.)</p>
<h5>Eggs</h5>
<p>So again, it&#8217;s audience and not content that is the true business asset. Your audience, whether it&#8217;s a literal database of registered users or a free-roaming anonymous swarm of eyeballs (as is the case with most free content sites) is the thing to guard.</p>
<p>Mark Twain said, &#8220;Put all of your eggs in one basket—and watch that basket!&#8221; Media companies already have all the eggs in the audience basket. How well are they watching it?</p>
<p>This gets us back to the main point, which is that the web makes it very easy for one site to leak its precious audience data to another. All it takes is one widget, one little snippet of Javascript, to open up the gates.</p>
<p>Take the gumball machine example again but fast forward to the present and swap roles. Now you&#8217;re the publisher of GumballGuru.com and companies buy advertising on your site to gain access to your audience. On each of the pages on your site you have a Facebook Like button that reports back to Facebook which of their users are regular visitors to your site. This allows Facebook to build a database of people interested in gumball machines. The like button earns you some traffic from Facebook. Woot. But in exchange, you hand over your users. You squander your eggs.</p>
<p>Now your advertisers have another way to reach their target audience. Instead of buying ads on GumballGuru.com—as well as on your competitor GumballManiac.com—they can go to Facebook and purchase ads targeted at both users who like GumballGuru and GumballManiac. And they can do this at a much better cost.</p>
<p>By the way, if you don&#8217;t know, this is a real thing. You can go to Facebook today and purchase advertising exactly this way.</p>
<p>The sad thing is that publishers have been an accomplice to a heist they don&#8217;t yet realize they&#8217;re the victim of. It&#8217;s true that Facebook can collect this sort of data in other ways but they could never gather it so quickly and in such detail without cooperation from sites that did the hard work of building the audiences in the first place.</p>
<p>I&#8217;m using Facebook only as an example. It&#8217;s probably safe to say that the majority of third party widget providers are harvesting user data in some way. <a href="http://www.addthis.com/privacy#.T8DrLHlYs1c" target="_blank">AddThis does</a>, <a href="http://sharethis.com/privacy" target="_blank">ShareThis does</a>, <a href="http://twitter.com/privacy" target="_blank">Twitter does</a>, <a href="http://www.linkedin.com/static?key=privacy_policy" target="_blank">LinkedIn does</a>. We shouldn&#8217;t vilify them. The fact that the data is being collected doesn&#8217;t mean it&#8217;s been used in nefarious ways. You should just understand that when you add a piece of third-party JavaScript to your site, in exchange for whatever service you&#8217;re getting you&#8217;re giving the provider access to your user base.</p>
<h5>It&#8217;s not the end of the world</h5>
<p>I&#8217;ve painted a stark picture and admittedly reached out to extremes in an effort to clearly illustrate the problem. Not all media outfits are critically exposed to this threat. Advertisers and media agencies are not shifting their entire budgets to Facebook and LinkedIn tomorrow. There&#8217;s still question about the value of social network advertising. Journalism still has value and publishers are still more than capable of cultivating important relationships with their audiences. Facebook is not going to replace The New York Times (although it may replace GumballGuru).</p>
<p>The goal of this post was to draw attention to two ideas. One, that in a digital world, data about your audience IS your audience. And two, that data on the web is incredibly exposed and prone to being shared in ways even the people building the websites aren&#8217;t aware of.</p>
<p>What&#8217;s the solution? I have no idea. Building a fence around your product isn&#8217;t the answer. Probably the best thing to do is to continue to focus on building products that solve problems for people. Products that people will pull into their lives. While at the same time being aware of who you&#8217;re competing with and who you&#8217;re sharing data with.</p>
<p>That should make for a good start.</p>
<img src="http://feeds.feedburner.com/~r/ravelrumba/~4/QdsG0J2hfDg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ravelrumba.com/blog/squander/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ravelrumba.com/blog/squander/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.535 seconds. --><!-- Cached page generated by WP-Super-Cache on 2013-05-23 04:52:40 -->
