<?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/"
	>

<channel>
	<title>Ryan Seddon</title>
	<atom:link href="https://www.thecssninja.com/feed" rel="self" type="application/rss+xml" />
	<link>https://www.thecssninja.com</link>
	<description></description>
	<lastBuildDate>Fri, 12 Jan 2018 17:00:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.5.20</generator>

<image>
	<url>https://www.thecssninja.com/wp-content/uploads/2016/05/cropped-logo-1-1-32x32.png</url>
	<title>Ryan Seddon</title>
	<link>https://www.thecssninja.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>An earth mosaic</title>
		<link>https://www.thecssninja.com/javascript/an-earth-mosaic</link>
		<comments>https://www.thecssninja.com/javascript/an-earth-mosaic#respond</comments>
		<pubDate>Fri, 12 Jan 2018 17:00:25 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1532</guid>
		<description><![CDATA[So a while back I created a twitter bot @earthin24 that generates a video of earth, as seen from himawari8 satellite, every day. To see the year out I wanted to create a combined video into a mosaic. &#x1f30f; Earth mosaic of all days captured so far for 2017 in 4k pic.twitter.com/VGf5qUct1M &#8212; Earth (@earthin24) &#8230; <a href="https://www.thecssninja.com/javascript/an-earth-mosaic" class="more-link">Continue reading<span class="screen-reader-text"> "An earth mosaic"</span></a>]]></description>
				<content:encoded><![CDATA[<p>So a while back I created a twitter bot <a href="http://twitter.com/earthin24">@earthin24</a> that generates a video of earth, as seen from himawari8 satellite, every day.<span id="more-1532"></span></p>
<p>To see the year out I wanted to create a combined video into a mosaic.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><img src="https://s.w.org/images/core/emoji/72x72/1f30f.png" alt="🌏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Earth mosaic of all days captured so far for 2017 in 4k <a href="https://t.co/VGf5qUct1M">pic.twitter.com/VGf5qUct1M</a></p>
<p>&mdash; Earth (@earthin24) <a href="https://twitter.com/earthin24/status/948698686715977728?ref_src=twsrc%5Etfw">January 3, 2018</a></p></blockquote>
<p><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>
<p>The generated videos aren&#8217;t that big so I never bothered to clean up after my script runs on my server which saved me having to generate them again, phew!</p>
<p>First I can get a quick count of all the videos I have:</p>
<pre><code>ls *.mp4 | wc -l
</code></pre>
<p>That gives me 196 videos captured so far. The next step is figuring out how I can arrange them into a tiled square.</p>
<pre><code>Math.sqrt(196) // lets me know I can tile in a nice even 14x14 arrangement.
</code></pre>
<h2>Creating a mosaic with ffmpeg</h2>
<p>This handy <a href="https://trac.ffmpeg.org/wiki/Create%20a%20mosaic%20out%20of%20several%20input%20videos">write up</a> not only saved me a lot of time but also broke down the often cryptic looking cli flags that <a href="https://www.ffmpeg.org/">ffmpeg</a> has. I won&#8217;t reiterate what the the syntax means I highly recommend you read the article to get a great break down of the command that follows.</p>
<p>Not wanting to hand write this syntax for a 196 videos I turn to scripting it, in js no less.</p>
<p>This is the output we want our js to generate so I can copy it into a shell script to execute.</p>
<pre><code>ffmpeg
        -i video.mp4 -i ...
        -filter_complex "
                color=s=720x720:c=black [base];
                [0:v] setpts=PTS-STARTPTS, scale=51x51 [vid0];
                [1:v] setpts=PTS-STARTPTS, scale=51x51 [vid1];
                //...
                [base][vid0] overlay=shortest=1 [tmp0];
                [tmp0][vid1] overlay=shortest=1:x=51 [tmp1];
                // ...
        "
        -c:v libx264 earth_mosaic.mp4
</code></pre>
<p>The first thing we can do is use the <a href="https://tldr.ostera.io/printf">printf</a> command to spread out <code>-i</code> in our command without having to do it 196 times.</p>
<pre><code>printf " -i %s" 2017*.mp4
</code></pre>
<p>This will iterate over any file matching the name 2017*.mp4 and output it as -i 20170620.mp4 and so on. So our command above can be changed to:</p>
<pre><code>ffmpeg
        `printf " -i %s" 2017*.mp4`
        -filter_complex "
                color=s=720x720:c=black [base];
                [0:v] setpts=PTS-STARTPTS, scale=154x154 [vid0];
                [1:v] setpts=PTS-STARTPTS, scale=154x154 [vid1];
                //...
                [base][vid0] overlay=shortest=1 [tmp0];
                [tmp0][vid1] overlay=shortest=1:x=154 [tmp1];
                // ...
        "
        -c:v libx264 earth_mosaic.mp4
</code></pre>
<p>The back tick means it&#8217;ll replace that command with whatever is returned via stdout.</p>
<h2>Generating the complex filter argument</h2>
<p>The first thing we need to do is generate the video stream slot and label them all appropriately so we can refer to them in the second step to actually overlay the scaled video in the correct [x,y] position in our black video canvas.</p>
<pre><code>var videos = Array(196).fill('vidya');

function slots(videos, scale) {
  return videos.reduce((acc, cur, idx) =&gt; {
    return acc + `[${idx}:v] setpts=PTS-STARTPTS, scale=${scale}x${scale} [vid${idx}];\n`
  }, '')
}
</code></pre>
<pre><code>var videos = Array(196).fill('vidya');
</code></pre>
<p>First we create an array that matches the length of the videos we have, 196.</p>
<p>Then we have a function that accepts the array and a scale argument.</p>
<pre><code>var scale = Math.round(resolution/Math.sqrt(videos.length));
</code></pre>
<p>This is a rounded whole number of our resolution 2160 for 4k and the square root of our videos which is 14, that gives us a video slot size of 154&#215;154 since we&#8217;re creating a square video of 2160&#215;2160.</p>
<pre><code>return videos.reduce((acc, cur, idx) =&gt; {
  return `${acc} [${idx}:v] setpts=PTS-STARTPTS, scale=${scale}x${scale} [vid${idx}];\n`
}, '')
</code></pre>
<p>Using Array#reduce to reduce the video array into a string returning our video slots we&#8217;ll use in the next step.</p>
<p>Now we&#8217;ve build up our 196 slots we can now overlay our videos into them and position them in the right spot on our video canvas.</p>
<pre><code>function overlay(videos, scale) {
  return videos.reduce((acc, cur, idx, arr) =&gt; {
    if (idx === 0) {
      return `${acc} [base][vid${idx}] overlay=shortest=1 [tmp${idx}];\n`;
    }

    var vidLen = videos.length;
    var sqrt = Math.sqrt(vidLen);
    var row = Math.floor(idx/sqrt);
    var tag = vidLen - 1 === idx ? '' : `[tmp${idx}];\n`;

    var y = scale * row;
    var x = scale * (idx % sqrt);

    return `${acc} [tmp${idx-1}][vid${idx}] overlay=x=${x}:y=${y} ${tag}`;
  }, '')
}
</code></pre>
<p>Much like the <code>slots</code> function this also accepts the videos array and a scale argument.</p>
<pre><code>if (idx === 0) {
  return `${acc} [base][vid${idx}] overlay=shortest=1 [tmp${idx}];\n`;
}
</code></pre>
<p>The first item we handle differently as we need to specify where the first video needs to overlay itself which is our <code>[base]</code> video canvas the default [x,y] coordinates default to [0,0] so we don&#8217;t specify it for the first item.</p>
<pre><code>var vidLen = videos.length;
var sqrt = Math.sqrt(vidLen);
var row = Math.floor(idx/sqrt);
var tag = vidLen-1 === idx ? '' : `[tmp${idx}];\n`;
</code></pre>
<p>The important variable here is <code>row</code> which we use <code>Math.floor</code> to figure out which row we&#8217;re currently on in our grid layout.</p>
<pre><code>var y = scale * row;
</code></pre>
<p>With the row figured out we can times our <code>scale</code> (154) by the current row to figure out our y position on our video canvas.</p>
<pre><code>var x = scale * (idx % sqrt);
</code></pre>
<p>The x position is slightly different as we need to adjust it for each item in the row so they sit next to each other. To do this we use the handy <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_()">remainder operator</a> (%) to let us know where in the row range the video should be overlayed e.g.</p>
<p>If we&#8217;re up to the second row say item 15:</p>
<pre><code>15 % 14 // 1
</code></pre>
<p>The following returns 1 letting us know we&#8217;re on the second row as 14 goes into 15 once leaving a remainder of 1 which is what the remainder operator (%) returns.</p>
<p>The last line is just building up the string we&#8217;ll return which will look like the following</p>
<pre><code>[base][vid0] overlay=shortest=1 [tmp0];
[tmp0][vid1] overlay=shortest=1:x=154 [tmp1];
// ...
</code></pre>
<p>Now we have our two functions to produce the slots and video overlays in the correct position we can now put the together into a function to output our ffmpeg command we want to copy into our shell script to run against out videos to produce the mosaic.</p>
<pre><code>function ffmpegcmd() {
  var resolution = 2160;
  var scale = Math.round(resolution/Math.sqrt(videos.length));
  var videos = Array(196).fill('vidya');

  return `ffmpeg \`printf " -i %s" 2017*.mp4\` -filter_complex "
    color=s=${resolution}x${resolution}:c=black [base];
    ${slots(videos, scale)}
    ${overlay(videos, scale)}
" -c:v libx264 earth_mosaic.mp4
`
}
</code></pre>
<p>As mentioned above this function will create the <code>videos</code> and <code>scale</code> arguments to be passed into <code>slots</code> and <code>overlay</code> functions. The string it returns is the same as shown at the start of the article.</p>
<p>One neat trick in the Firefox and Chrome dev tools is you can wrap <code>ffmpegcmd()</code> in the <code>copy()</code> function to take the output and put it on your clipboard instead.</p>
<pre><code>copy(ffmpegcmd()) // returns undefined and adds output to your clipboard instead
</code></pre>
<h2>ffmpeg is amazing</h2>
<p><a href="https://www.ffmpeg.org/">ffmpeg</a> is an incredibly powerful tool that made this possible and it&#8217;s amazing it just worked with combining 196 videos like that.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/javascript/an-earth-mosaic/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How I built my earthin24 twitter bot</title>
		<link>https://www.thecssninja.com/hacking/how-i-built-my-earthin24-twitter-bot</link>
		<comments>https://www.thecssninja.com/hacking/how-i-built-my-earthin24-twitter-bot#respond</comments>
		<pubDate>Sat, 26 Aug 2017 05:43:36 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[golang]]></category>
		<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1518</guid>
		<description><![CDATA[At work my team have recently taken over a golang app. To learn a new language I like to build something that keeps my interest and by the nature of exploring something of interest you tend to cover way more surface area of a language this way. &#x1f30f; 12pm 25th August &#8211; 12pm 26th August &#8230; <a href="https://www.thecssninja.com/hacking/how-i-built-my-earthin24-twitter-bot" class="more-link">Continue reading<span class="screen-reader-text"> "How I built my earthin24 twitter bot"</span></a>]]></description>
				<content:encoded><![CDATA[<p>At work my team have recently taken over a <a href="https://golang.org/">golang</a> app. To learn a new language I like to build something that keeps my interest and by the nature of exploring something of interest you tend to cover way more surface area of a language this way.<span id="more-1518"></span></p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><img src="https://s.w.org/images/core/emoji/72x72/1f30f.png" alt="🌏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 12pm 25th August &#8211; 12pm 26th August 2017 <a href="https://t.co/KUL3iz4goQ">pic.twitter.com/KUL3iz4goQ</a></p>
<p>&mdash; Earth (@earthin24) <a href="https://twitter.com/earthin24/status/901278093364670464">August 26, 2017</a></p></blockquote>
<p><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p>
<p>Check out the code on <a href="https://github.com/ryanseddon/earthin24">github</a>.</p>
<h2>Space is awesome</h2>
<p>If you follow me on twitter you may notice I retweet a lot of space stuff so building something space related was inevitable. Way back something came up in my twitter feed, <a href="https://glittering.blue/">glittering.blue</a>, which is an awesome site that has a video of earth over 24hrs as seen from the incredible japanese <a href="http://himawari8.nict.go.jp/">himawari8</a> satellite. This satellite is in <a href="https://en.wikipedia.org/wiki/Geostationary_orbit">geostationary orbit</a> and gives us a nice fixed view of Australia &#8211; where I&#8217;m from.</p>
<h2>Live background image of earth</h2>
<p>Initially I wrote a go cli tool to download the latest image of earth available from himawari8 and set it as my background, I was heavily inspired by the code from <a href="https://twitter.com/vruba">Charlie Loyd&#8217;s</a> python script to download the images found on <a href="https://glittering.blue/about">glittering.blue</a>.</p>
<h2>Why not a video every day</h2>
<p>To extend the idea I thought why not generate a video everyday and post it to twitter, I did a search and found that a decent twitter name was still available, <a href="https://twitter.com/earthin24">@earthin24</a>. If you follow you&#8217;ll get a gorgeous video once a day.</p>
<h2>Things I learned</h2>
<p>My background is in front-end development so Go was new territory for me here&#8217;s some things I learned along the way while creating this bot.</p>
<h2>goroutines and WaitGroup</h2>
<p>Initially I had the for loop requesting each frame for the video synchronously which took quite a while as it would fetch 144 images, luckily go is really good at concurrency so I explored the <a href="https://golang.org/pkg/sync/">sync package</a> to help track goroutines and only exit once all images have downloaded. Using concurrency would allow me to create 144 goroutines very quickly and concurrently download all of them over a much shorter time period.</p>
<p>Starting a goroutine is as simple as prefixing a function with the <code>go</code> keyword.</p>
<pre><code>for i, _ := range frames {
    go getImage(framePath(first), i)
}
</code></pre>
<p>However the process won&#8217;t wait for all goroutines to finish before exiting so we need to use <code>sync.WaitGroup</code></p>
<pre><code>func main() {
     var wg sync.WaitGroup

     for i, _ := range frames {
          wg.Add(1)
          go getImage(framePath(first), i, &amp;wg)
     }

     wg.Wait()
}
</code></pre>
<p>For each goroutine we increment the <code>WaitGroup</code> counter using <code>Add</code> and at the end of the block we use <code>wg.Wait()</code> which blocks from exiting until the counter has reached zero.</p>
<p>You&#8217;ll also notice I pass a pointer reference to <code>WaitGroup</code> <code>&amp;wg</code> to the <code>getImage</code> method so I can call <code>wg.Done()</code> outside of the current function scope. For more on pointers see <a href="http://winterflower.github.io/2017/08/20/the-asterisk-and-the-ampersand/">the asterisk and the ampersand &#8211; a golang tale</a>.</p>
<pre><code>func getImage(url string, name int, wg *sync.WaitGroup, date time.Time) {
     defer wg.Done()
     //...
}
</code></pre>
<p><code>wg.Done()</code> decrements the counter once it&#8217;s done. The <code>defer</code> keyword means to delay the function call until the end of the function. It would be the same as just calling <code>wg.Done()</code> at the end of the function but it is idiomatic Go to do this at the beginning using <code>defer</code>.</p>
<h2>Automated hands free artisanal space tweets</h2>
<p>So far I&#8217;ve been running this for a few months and it has posted daily without me needing to think about it. It&#8217;s pretty straight forward but there&#8217;s a few more learning&#8217;s when I set up the cronjob.</p>
<h2>bash date</h2>
<p>On MacOS you can type <code>date</code> to get the current date in your terminal, when my cronjob runs my bash script it needs to get today-1 in the following format YYYYMMDD:</p>
<pre><code>date -v-1d +%Y%m%d
</code></pre>
<p><code>-v</code> making it very easy by being able to subtract a day from the current date using <code>-1d</code> and then using the <code>+%Y%m%d</code> for the date output_format so a date could come out like so <code>20170819</code>.</p>
<p>The only problem was when I tried to run this on my Linux based server the date command failed! Seems MacOS uses its own date version different from Linux, to subtract a day we need to use the <code>--date="1 day ago"</code> argument.</p>
<pre><code>date --date="1 day ago" +%Y%m%d
</code></pre>
<h2>ffmpeg</h2>
<p>The go app downloads all the frames ready to be passed into ffmpeg to actually generate the mp4 video that&#8217;s posted to twitter &amp; instagram. Since some of the frames are dropped due to the satellite not processing the image at the time the sequential file naming of the files can sometimes skip some numbers throwing off ffmpeg. If you try and pass a sequentially ordered file name pattern to it, ffmpeg will error out when looking for a file name that may be missing.</p>
<p>To get around this we use <code>-pattern_type glob</code> argument for ffmpeg and then just use a simple <code>*.png</code> wildcard.</p>
<p>Instead of <code>ffmpeg -i %4d.png</code> we need to do <code>ffmpeg -pattern_type glob -i '*.png'</code> to cater for the potential non-sequential frames.</p>
<h2>Amazing</h2>
<p>It continues to blow my mind that I can get images from a satellite and actually do this, the Japanese Space Ageny (JAXA) is absolutely incredible for releasing this to the public.</p>
<h2>Bonus</h2>
<p>I also extended this to instagram <a href="https://www.instagram.com/earthintwentyfour/">@earthintwentyfour</a> so if you don&#8217;t like twitter follow me there.</p>
<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div>
</div>
<p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BYPbtjAA4QC/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank"><img src="https://s.w.org/images/core/emoji/72x72/1f30f.png" alt="🌏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 12pm 25th August &#8211; 12pm 26th August 2017</a></p>
<p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by Earth (@earthintwentyfour) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2017-08-26T03:00:28+00:00">Aug 25, 2017 at 8:00pm PDT</time></p>
</div>
</blockquote>
<p><script async defer src="//platform.instagram.com/en_US/embeds.js"></script></p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/hacking/how-i-built-my-earthin24-twitter-bot/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Stop iOS10 browser auto-locking</title>
		<link>https://www.thecssninja.com/html5/stop-ios10-safari-auto-locking</link>
		<comments>https://www.thecssninja.com/html5/stop-ios10-safari-auto-locking#respond</comments>
		<pubDate>Tue, 27 Sep 2016 04:19:30 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[html5]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1497</guid>
		<description><![CDATA[I had twitter open the other day and I had to put my phone down and attend to something else, the timeline happened to be on a looping gif video and I noticed when I came back that my phone was still awake and unlocked the fact that video was in view kept the phone &#8230; <a href="https://www.thecssninja.com/html5/stop-ios10-safari-auto-locking" class="more-link">Continue reading<span class="screen-reader-text"> "Stop iOS10 browser auto-locking"</span></a>]]></description>
				<content:encoded><![CDATA[<p>I had twitter open the other day and I had to put my phone down and attend to something else, the timeline happened to be on a looping gif video and I noticed when I came back that my phone was still awake and unlocked the fact that video was in view kept the phone awake and this got me thinking about the new <code>playsinline</code> attribute that has been enabled for <code>&lt;video&gt;</code> elements in iOS10 Safari.<span id="more-1497"></span></p>
<h2>TL;DR;</h2>
<pre><code>&lt;video autoplay loop muted playsinline src="video.mp4" /&gt;
</code></pre>
<p><strong>Update:</strong> This trick no longer seems to work in the latest iOS however a clever person has an alternative project called <a href="https://github.com/richtr/NoSleep.js">NoSleep.js</a> that works. Similar idea but it randomly scrubs the video and seems to require an audio source to keep the screen alive.</p>
<p>Check out the live <a href="https://thecssninja.com/demo/ios-stop-auto-lock/">demo</a>.</p>
<p>The demo uses a base64 encoded mp4 video from Mathias Bynens <a href="https://github.com/mathiasbynens/small/blob/master/mp4.mp4">small repo</a> which makes it around 1.2 &#8211; 1.3kb.</p>
<h2>playsinline attribute</h2>
<p>Finally within iOS10 on an iPhone you can now autoplay a silent video using the new <code>playsinline</code> attribute and avoid the fullscreen player that we&#8217;re all used to on iPhones. Check out the <a href="https://webkit.org/blog/6784/new-video-policies-for-ios/">webkit blog post</a> on their new <code>&lt;video&gt;</code> policies.</p>
<h2>Hiding the video</h2>
<p>In order for the phone to stay awake the video needs to be visible and within the viewport so obvious things like positioning off canvas, <code>visibility:hidden</code> and <code>opacity: 0</code> don&#8217;t work. However we can use the following CSS to make it invisible to the human eye but not to the browser and therefore keep the phone from auto-locking.</p>
<pre><code>video {
  position: absolute; // take video out of document flow
  z-index: -1; // position it behind other layers
  opacity: 0.01; // Lowest valid opacity value to make it invisible
  width: 1px; // Reduce default video dimensions to smallest non 0 value
  height: 1px;
}
</code></pre>
<h2>Chrome iOS</h2>
<p><del datetime="2016-11-04T05:33:10+00:00">Chrome seems to have a bug where it won&#8217;t actually autoplay the video and therefore will go to sleep as the video isn&#8217;t playing to keep it awake. Hit me up on twitter if you know how to fix or why it doesn&#8217;t work. </del></p>
<p>Seems in the latest chrome iOS release this now works just fine!</p>
<h2>Toggle auto-locking</h2>
<p>If anyone wants to run with this you could easily make it start and stop the video via user interaction say you have a timer app you could remove the <code>autoplay</code> attribute and just call <code>play()</code> or <code>pause()</code> on the video to toggle if the phone should sleep or not.</p>
<p>Have fun and don&#8217;t abuse this.</p>
<p><!-- Hello as you can see I've added the video here so in iOS10 safari this webpage won't go to sleep --></p>
<p><video autoplay loop muted playsinline style="position: fixed; z-index: -1; bottom: 0; opacity: 0.01; width: 1px; height: 1px" src="data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAGm1kYXQAAAGzABAHAAABthBgUYI9t+8AAAMNbW9vdgAAAGxtdmhkAAAAAMXMvvrFzL76AAAD6AAAACoAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABhpb2RzAAAAABCAgIAHAE/////+/wAAAiF0cmFrAAAAXHRraGQAAAAPxcy++sXMvvoAAAABAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAgAAAAIAAAAAAG9bWRpYQAAACBtZGhkAAAAAMXMvvrFzL76AAAAGAAAAAEVxwAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAABaG1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAShzdGJsAAAAxHN0c2QAAAAAAAAAAQAAALRtcDR2AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgACABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAAXmVzZHMAAAAAA4CAgE0AAQAEgICAPyARAAAAAAMNQAAAAAAFgICALQAAAbABAAABtYkTAAABAAAAASAAxI2IAMUARAEUQwAAAbJMYXZjNTMuMzUuMAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAAABAAAAAQAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAASAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1My4yMS4x"><br />
</video></p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/html5/stop-ios10-safari-auto-locking/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="https://github.com/mathiasbynens/small/blob/master/mp4.mp4" length="0" type="video/mp4" />
		</item>
		<item>
		<title>Changing content type of a file in a FormData request with cURL</title>
		<link>https://www.thecssninja.com/hacking/content-type-formdata-curl</link>
		<comments>https://www.thecssninja.com/hacking/content-type-formdata-curl#respond</comments>
		<pubDate>Mon, 25 Jul 2016 16:00:02 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1490</guid>
		<description><![CDATA[The other day I had to test an API response based on a file having the incorrect Content-Type of a FormData PUT request. After some digging in cURL I found the -F &#38; --form flag to set the file and its content type. If you don&#8217;t specify the content type it will be inferred from &#8230; <a href="https://www.thecssninja.com/hacking/content-type-formdata-curl" class="more-link">Continue reading<span class="screen-reader-text"> "Changing content type of a file in a FormData request with cURL"</span></a>]]></description>
				<content:encoded><![CDATA[<p>The other day I had to test an API response based on a file having the incorrect Content-Type of a <a href="https://developer.mozilla.org/en/docs/Web/API/FormData">FormData</a> PUT request.<span id="more-1490"></span></p>
<p>After some digging in cURL I found the <code>-F</code> &amp; <code>--form</code> flag to set the file and its content type. If you don&#8217;t specify the content type it will be inferred from the files extension.</p>
<pre><code class="bash">curl -X PUT https://example.com/api/thing --form 'csv=@stocks.csv;type=text/plain'
</code></pre>
<p>Using <code>--form</code> we pass the form data with the following format:</p>
<pre><code class="bash">[name]=@[path/to/file];type=[override content-type]
</code></pre>
<p>Given your endpoints validation cURL will return whatever response it gets from the API.</p>
<h2>Trace ascii output</h2>
<pre><code class="bash">curl -X PUT https://example.com/api/thing --form 'csv=@stocks.csv;type=text/plain' --trace-ascii -
</code></pre>
<p>If you want to see a nicely formatted output you can pass the <code>--trace-ascii -</code> flag which will show the request body as well as the response and all headers. The extra <code>-</code> means it will output to <code>stdout</code> rather than write to a file.</p>
<pre><code>curl -X PUT http://example.com --form 'csv=@stocks.csv;type=text/plain' --trace-ascii -
== Info: Rebuilt URL to: http://example.com/
== Info:   Trying 93.184.216.34...
== Info: Connected to example.com (93.184.216.34) port 80 (#0)
=&gt; Send header, 267 bytes (0x10b)
0000: PUT / HTTP/1.1
0010: Host: example.com
0023: User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; T
0063: rident/5.0)
0070: Accept: */*
007d: Referer:
0088: Content-Length: 472
009d: Expect: 100-continue
00b3: Content-Type: multipart/form-data; boundary=--------------------
00f3: ----25e7f48e0eecccf4
0109:
&lt;= Recv header, 23 bytes (0x17)
0000: HTTP/1.1 100 Continue
=&gt; Send data, 139 bytes (0x8b)
0000: --------------------------25e7f48e0eecccf4
002c: Content-Disposition: form-data; name="csv"; filename="stocks.csv
006c: "
006f: Content-Type: text/plain
0089:
=&gt; Send data, 285 bytes (0x11d)
0000: Date,Open,High,Low,Close,Volume,Adj Close.2016-07-19,305.00,305.
0040: 929993,265.00,288.00,103500,288.00.2016-07-18,286.73999,308.25,2
0080: 85.50,299.00,118000,299.00.2016-07-15,262.019989,267.26001,259.0
00c0: 40009,267.209991,28100,267.209991.2016-07-14,244.220001,246.00,2
0100: 36.800003,243.75,38400,243.75
=&gt; Send data, 48 bytes (0x30)
0000:
0002: --------------------------25e7f48e0eecccf4--
&lt;= Recv header, 33 bytes (0x21)
0000: HTTP/1.1 405 Method Not Allowed
&lt;= Recv header, 31 bytes (0x1f)
0000: Cache-Control: max-age=604800
&lt;= Recv header, 37 bytes (0x25)
0000: Date: Wed, 20 Jul 2016 04:30:16 GMT
&lt;= Recv header, 40 bytes (0x28)
0000: Expires: Wed, 27 Jul 2016 04:30:16 GMT
&lt;= Recv header, 27 bytes (0x1b)
0000: Server: EOS (lax004/2812)
&lt;= Recv header, 19 bytes (0x13)
0000: Content-Length: 0
== Info: HTTP error before end of send, stop sending
&lt;= Recv header, 2 bytes (0x2)
0000:
== Info: Closing connection 0
</code></pre>
<p>Hope this helps someone else in the same situation.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/hacking/content-type-formdata-curl/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>So how does the browser actually render a website</title>
		<link>https://www.thecssninja.com/browser/jsconfeu15</link>
		<comments>https://www.thecssninja.com/browser/jsconfeu15#comments</comments>
		<pubDate>Wed, 04 Nov 2015 09:47:28 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[browser]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1458</guid>
		<description><![CDATA[September 2015 I had the privelage of giving a talk at JSConfEU 2015 in Berlin. My topic was about what the browser actually does to construct your website and show it on the screen. This is a topic I&#8217;ve been digging into for a few years now and it was really fun trying to distill &#8230; <a href="https://www.thecssninja.com/browser/jsconfeu15" class="more-link">Continue reading<span class="screen-reader-text"> "So how does the browser actually render a website"</span></a>]]></description>
				<content:encoded><![CDATA[<p>September 2015 I had the privelage of giving a talk at JSConfEU 2015 in Berlin. My topic was about what the browser actually does to construct your website and show it on the screen. This is a topic I&#8217;ve been digging into for a few years now and it was really fun trying to distill it into a ~25 minute talk.<span id="more-1458"></span></p>
<h2><a href="https://www.youtube.com/embed/SmE4OwHztCc">Video</a></h2>
<p><iframe width="560" height="315" src="https://www.youtube.com/embed/SmE4OwHztCc" frameborder="0" allowfullscreen style="min-height: 457px;"></iframe></p>
<h2><a href="https://speakerdeck.com/ryanseddon/how-the-browser-actually-renders-a-website">Slides</a></h2>
<p><script async class="speakerdeck-embed" data-id="e9f834e5fa5e4156b4e3692cf6ff3a8a" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script></p>
<h2>Resources</h2>
<p>Here&#8217;s a list of resources that really helped me understand what the browser is doing and complexity around how it gets your website displayed on the screen.</p>
<ul>
<li><a href="http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/">How Browsers Work</a></li>
<li><a href="https://www.youtube.com/watch?v=RVnARGhhs9w">Rendering in WebKit – Eric Seidel (video)</a></li>
<li><a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/adding-interactivity-with-javascript?hl=en">Critical rendering path</a></li>
<li><a href="http://www.webkit.org/projects/layout/index.html">Series of posts about WebKit&#8217;s layout and rendering</a></li>
<li><a href="https://docs.google.com/presentation/d/1ZRIQbUKw9Tf077odCh66OrrwRIVNLvI_nhLm2Gi__F0/embed?start=false&amp;loop=false&amp;delayms=3000#slide=id.p">How WebKit works</a></li>
<li><a href="https://www.youtube.com/watch?v=ZTnIxIA5KGw">Visualising browser layout (video)</a></li>
<li><a href="https://www.youtube.com/watch?v=AKZ2fj8155I">Another layout visualisation (video)</a></li>
<li><a href="http://limpet.net/mbrubeck/2014/08/08/toy-layout-engine-1.html">Building your own browser engine</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/browser/jsconfeu15/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Modernizr 3: A beginners guide</title>
		<link>https://www.thecssninja.com/css/modernizr-3-a-beginners-guide</link>
		<comments>https://www.thecssninja.com/css/modernizr-3-a-beginners-guide#comments</comments>
		<pubDate>Thu, 24 Sep 2015 15:18:09 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[css]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1444</guid>
		<description><![CDATA[Just in time for the launch of Modernizr 3 I&#8217;ve been working on a beginners course. This is aimed at people who are getting started in web development and want to know how they can use Modernizr in their workflows. I have 5 vouchers to give away that will give you free access to the &#8230; <a href="https://www.thecssninja.com/css/modernizr-3-a-beginners-guide" class="more-link">Continue reading<span class="screen-reader-text"> "Modernizr 3: A beginners guide"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Just in time for the launch of <a href="https://modernizr.com/news/modernizr-3-new-release-site" target="_blank">Modernizr 3</a> I&#8217;ve been working on a <a href="https://www.sitepoint.com/premium/courses/modernizr-3-2905" title="Modernizr 3 course" target="_blank">beginners course</a>. This is aimed at people who are getting started in web development and want to know how they can use Modernizr in their workflows.<span id="more-1444"></span></p>
<p><a href="https://www.sitepoint.com/premium/courses/modernizr-3-2905" title="Modernizr 3 course" target="_blank"><img src="https://d3rj1gznkm47xj.cloudfront.net/37691fd4-f791-4343-9ebc-9bb779de3d46.png" alt="Modernizr 3 course promo image" /></a></p>
<p><del datetime="2015-09-24T15:32:32+00:00">I have 5 vouchers to give away that will give you free access to the course! The first 5 people to tweet the <a href="https://www.sitepoint.com/premium/courses/modernizr-3-2905" title="Modernizr 3 course" target="_blank">course link</a> with the hashtag <strong>#modernizr3course</strong> will get a voucher link to access the course for free.</del></p>
<p>They&#8217;re <a href="https://twitter.com/hashtag/modernizr3course?f=tweets&#038;vertical=default&#038;src=hash" target="_blank">all gone</a> thanks for playing :).</p>
<p>Check out the <a href="https://www.sitepoint.com/premium/courses/modernizr-3-2905" title="Modernizr 3 course" target="_blank">course now</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/css/modernizr-3-a-beginners-guide/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>UITableView in JavaScript, list view with re-usable cells using flexbox</title>
		<link>https://www.thecssninja.com/css/scrolllistview</link>
		<comments>https://www.thecssninja.com/css/scrolllistview#comments</comments>
		<pubDate>Tue, 22 Jul 2014 11:19:05 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1410</guid>
		<description><![CDATA[If you&#8217;re familiar with iOS development you will know that a UITableView is very efficient when displaying a list of data. A simplification of what it does is display enough cells to fill the viewport plus a few more either side. As you scroll it re-uses cells that are now out of the viewport so &#8230; <a href="https://www.thecssninja.com/css/scrolllistview" class="more-link">Continue reading<span class="screen-reader-text"> "UITableView in JavaScript, list view with re-usable cells using flexbox"</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;re familiar with iOS development you will know that a UITableView is very efficient when displaying a list of data. A simplification of what it does is display enough cells to fill the viewport plus a few more either side. As you scroll it re-uses cells that are now out of the viewport so a list with thousands of items will only ever use a fixed amount of cells. Highly recommend reading The fine art of <a href="http://alexeckermann.com/blog/legacy/the-fine-art-of-uitableviews">UITableViews</a>. Now this has certainly been done before in JavaScript, the best known project being <a href="http://airbnb.github.io/infinity/">infinity.js</a>, but my approach takes an interesting turn, I avoid heavy DOM operations by using flexbox.<span id="more-1410"></span></p>
<h2>How does flexbox help?</h2>
<p>If you&#8217;re familiar with flexbox you may have come across the <code>order</code> property which allows you to reorder flex items in a flexbox container and this is the magic that allows us to create reusable list items without having to actually rip the element out of the DOM and then re-inject it in the right place as a user scrolls.</p>
<p>Check out the project on <a href="https://github.com/ryanseddon/ScrollListView">github</a> and have a look at the <a href="http://ryanseddon.github.io/ScrollListView">live demo</a></p>
<h2>How does it work</h2>
<p>The whole technique boils down to the flexbox <code>order</code> property which allows reordering of a flex item within a flexbox container, essentially I can say to the browser repaint the DOM node here instead of where it is in the actual DOM structure.</p>
<p><img src="https://www.thecssninja.com/wp-content/uploads/2014/03/flexbox-order-property.png" alt="flexbox order property example in Chrome Dev Tools" width="609" height="204" class="article-img main-img" /></p>
<p>In the above image the top four cells are actually rendered beneath the last four as we&#8217;ve informed the browser to repaint them below using the <code>order</code> property.</p>
<p>On scroll I check the scroll position. When the user has scrolled more than the threshold, (3 cells are out of the viewport) I then set the order property on the element that can now be painted at the bottom of the list. To then render the correct data the order number and the array index is passed off to <code>renderCellFn()</code>.</p>
<p>First we check the user is actually scrolling and in which direction.</p>
<div class="syntax code">
<pre><span class="nx">onScroll</span><span class="o">:</span> <span class="kd">function</span> <span class="nx">onScroll</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">scrollTop</span> <span class="o">=</span> <span class="nx">body</span><span class="p">.</span><span class="nx">scrollTop</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">direction</span> <span class="o">=</span> <span class="nx">scrollTop</span> <span class="o">-</span> <span class="nx">lastScrollTop</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">checkCells</span><span class="p">();</span>
<span class="p">}</span>
</pre>
</div>
<p>The scrolling direction is determined by comparing the last scroll top position to the current. The <code>onScroll</code> method is what the <code>scroll</code> event calls directly.</p>
<div class="syntax code">
<pre><span class="nx">getCurrentCell</span><span class="o">:</span> <span class="kd">function</span> <span class="nx">getCurrentCell</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">cells</span><span class="p">[</span><span class="nx">count</span> <span class="o">%</span> <span class="k">this</span><span class="p">.</span><span class="nx">cells</span><span class="p">.</span><span class="nx">length</span><span class="p">];</span>
<span class="p">}</span>
</pre>
</div>
<p>In <code>checkCells()</code> the <code>getCurrentCell()</code> method is called to determine the current cell by using the modulus (<code>%</code>) operator. Previously I had some over complicated maths to determine which of the re-useable cells I could use but all I really needed was modulus. What it does is divide the current count of &#8220;cells&#8221; that are out of view by the number of re-useable cells and returns the remainder which is the cell I can then reorder and render the data in.</p>
<p>Once we have the correct cell to use we then determine if it&#8217;s far enough out of the viewport to be able to use by calling <code>isTopElementOutOfViewport()</code>.</p>
<div class="syntax code">
<pre><span class="nx">isTopElementOutOfViewport</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">el</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">elemPostion</span> <span class="o">=</span> <span class="nx">el</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">(),</span>
      <span class="nx">bottomOffset</span> <span class="o">=</span> <span class="nx">elemPostion</span><span class="p">.</span><span class="nx">bottom</span> <span class="o">-</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">container</span><span class="p">.</span><span class="nx">offsetTop</span> <span class="o">-</span> <span class="nx">body</span><span class="p">.</span><span class="nx">scrollTop</span><span class="p">);</span>

  <span class="k">return</span> <span class="o">!!</span><span class="nx">elemPostion</span> <span class="o">&amp;&amp;</span> <span class="nx">bottomOffset</span> <span class="o">&lt;=</span> <span class="o">-</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">cellReorderThreshold</span><span class="p">());</span>
<span class="p">}</span>
</pre>
</div>
<p>To determine if it&#8217;s out of viewport we use the <code>getBoundingClientRect()</code> which will return a bunch of metrics about the cell we&#8217;re looking to use. This sort of operation is normally expensive and will cause a layout to trigger but since our actual DOM is so small it&#8217;s insignificant for the browser to do.</p>
<div class="syntax code">
<pre><span class="k">this</span><span class="p">.</span><span class="nx">cellsOutOfViewportCount</span><span class="o">++</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">cellIndex</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">cellsOutOfViewportCount</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">elementStyle</span><span class="p">.</span><span class="nx">paddingTop</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">elementStyle</span><span class="p">.</span><span class="nx">paddingTop</span> <span class="o">||</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">CELLHEIGHT</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentCell</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">orderProp</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">cellIndex</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderCell</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">currentCell</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">cellIndex</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</pre>
</div>
<p>If the top cell is far enough out of view then we can trigger the reorder, render the new data and increase the lists top padding to maintain the correct scroll position.</p>
<h2>Why not use requestAnimationFrame?</h2>
<p>You may be asking why I don&#8217;t use <code>requestAnimationFrame</code> when calling <code>checkCells()</code>. In Firefox the performance degraded massively and I&#8217;m not entirely sure why. So I&#8217;ve avoided it for the time being.</p>
<h2>Performance and visualising the technique</h2>
<p><iframe width="620" height="349" class="iframe-fix" src="//www.youtube.com/embed/in2miiynjks?vq=hd1080" frameborder="0" allowfullscreen></iframe></p>
<p>In the screencast I show performance benefits of this technique when working with asynchronously loading data and comparing it with a regular technique for loading async data. I also cover and visualise how the cells are being reordered using the awesome chrome://tracing tool.</p>
<h2>TL;DR Gif</h2>
<p><img src="https://i.imgur.com/jWUuhJO.gif" alt="Showing the cell re-ordering in chrome://tracing" width="609" class="article-img main-img" /></p>
<h2>Needs battle testing</h2>
<p>The code base is fairly beta and quite usable but there is still some work to do, as I&#8217;ll list below:</p>
<p>Some of the issue and limitations are:</p>
<ul>
<li>It only works with fixed height cells.</li>
<li>It has to be a flexbox supporting browser which is pretty good but no &lt;IE10</li>
<li>iOS won&#8217;t trigger scroll events until after the page has stopped scrolling, this will require the use of a scrolling library.</li>
<li>Accessibility?</li>
<li>Only reacts to scroll events, keyboard users won&#8217;t see list updated.</li>
<li>Doesn&#8217;t render the cells if you refresh and the browser re-instates the scroll position.</li>
</ul>
<h2>We need this built into the Web Platform</h2>
<p>If we want web apps to be performant then this elaborate hack should be built in to the Web Platform allowing the browser to handle the logic behind re-using cells without killing performance. We could introduce a property on <code>ol/ul</code> or even have a new element that treats any children as a re-usable cell.</p>
<p>There are some early editor drafts of specs that may help with this, the two I know of are <a href="https://dev.w3.org/csswg/css-containment/">css-containment</a> and the <a href="https://tabatkins.github.io/specs/css-will-change/">will-change</a> property.</p>
<p>Some of the MV* frameworks also have components for working with large data sets but most have the restriction of fixed height cells due to heavy recalculation issues when using variable height cells. Ember has <a href="https://github.com/emberjs/list-view">list-view</a>, Angular has <a href="https://github.com/jankuca/ng-scroller">ng-scroller</a>. Both of these libraries remove and re-inject DOM nodes.</p>
<p>Paul Irish has compiled a good list of libraries and list of Do&#8217;s and Dont&#8217;s to reduce jank when <a href="https://plus.google.com/+PaulIrish/posts/Ee53Gg6VCck">scrolling</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/css/scrolllistview/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Creating a sunrise alarm with the LIFX lightbulb</title>
		<link>https://www.thecssninja.com/hacking/sunrise-lifx</link>
		<comments>https://www.thecssninja.com/hacking/sunrise-lifx#comments</comments>
		<pubDate>Fri, 18 Apr 2014 14:00:16 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1436</guid>
		<description><![CDATA[If you&#8217;re unfamiliar with LIFX it&#8217;s basically a smart wifi enabled led lightbulb. Lifx recently released a Ruby gem to communicate with it and give it instructions. With winter looming in the southern hemisphere and the sun being up for what feels like 4 seconds at that time of year it&#8217;s really hard to get &#8230; <a href="https://www.thecssninja.com/hacking/sunrise-lifx" class="more-link">Continue reading<span class="screen-reader-text"> "Creating a sunrise alarm with the LIFX lightbulb"</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;re unfamiliar with <a href="https://lifx.co">LIFX</a> it&#8217;s basically a smart wifi enabled led lightbulb. Lifx recently released a Ruby <a href="https://github.com/LIFX/lifx-gem">gem</a> to communicate with it and give it instructions. With winter looming in the southern hemisphere and the sun being up for what feels like 4 seconds at that time of year it&#8217;s really hard to get up in the morning so I investigated using their gem to simulate a sunrise effect in time with my alarm on my phone.<span id="more-1436"></span></p>
<h2>TL;DR</h2>
<p>I have a cronjob on my raspberry pi that triggers 2 mins before my alarm to run some ruby code that&#8217;ll trigger a slow fade in of colours to simulate a sunrise. The light will go from a very dim red all the way through until a bright yellow almost white light. Code is on <a href="https://github.com/ryanseddon/sunrise-lifx">github</a>.</p>
<h2>What you&#8217;ll need</h2>
<ul>
<li>Lifx bulb</li>
<li>A server or computer that is always on. I have a raspberry pi</li>
<li>Any alarm, I use the rise app that plays nice bird chirping sounds.</li>
</ul>
<h2>The code</h2>
<p>At the moment lifx only has official libraries for Java, Objective-C &amp; Ruby. Being that I&#8217;m diving into ruby at the moment this was perfect. There is a unofficial <a href="https://github.com/magicmonkey/lifxjs">node library</a> that you could use too if you&#8217;re afraid of anything that&#8217;s not JavaScript.</p>
<div class="syntax code">
<pre><span class="nb">require</span> <span class="s1">'lifx'</span>

<span class="n">label</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span>
<span class="n">time</span> <span class="o">=</span> <span class="no">ARVG</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">||=</span> <span class="mi">120</span>

<span class="n">client</span> <span class="o">=</span> <span class="ss">LIFX</span><span class="p">:</span><span class="ss">:Client</span><span class="o">.</span><span class="n">lan</span>
<span class="n">client</span><span class="o">.</span><span class="n">discover!</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>
    <span class="n">c</span><span class="o">.</span><span class="n">lights</span><span class="o">.</span><span class="n">with_label</span><span class="p">(</span><span class="n">label</span><span class="p">)</span>
<span class="k">end</span>

<span class="n">client</span><span class="o">.</span><span class="n">lights</span><span class="o">.</span><span class="n">turn_on</span>
<span class="n">light</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">lights</span><span class="o">.</span><span class="n">with_label</span><span class="p">(</span><span class="n">label</span><span class="p">)</span>

<span class="n">fade_time</span> <span class="o">=</span> <span class="n">time</span> <span class="c1"># time in seconds</span>
<span class="n">interval</span> <span class="o">=</span> <span class="mi">6</span> <span class="c1"># run every 6 seconds</span>
<span class="n">increment</span> <span class="o">=</span> <span class="n">fade_time</span><span class="o">/</span><span class="n">interval</span>
<span class="n">luminence</span> <span class="o">=</span> <span class="mi">0</span><span class="o">.</span><span class="mo">01</span>
<span class="n">luminence_increment</span> <span class="o">=</span> <span class="mi">1</span><span class="o">/</span><span class="n">increment</span><span class="o">.</span><span class="n">to_f</span>
<span class="n">hue_increment</span> <span class="o">=</span> <span class="mi">60</span><span class="o">/</span><span class="n">increment</span> <span class="c1"># Fade from red to bright yellow 0 to 60</span>
<span class="n">hue</span> <span class="o">=</span> <span class="n">hue_increment</span>

<span class="n">light</span><span class="o">.</span><span class="n">set_color</span><span class="p">(</span><span class="ss">LIFX</span><span class="p">:</span><span class="ss">:Color</span><span class="o">.</span><span class="n">hsl</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">luminence</span><span class="p">),</span> <span class="ss">duration</span><span class="p">:</span> <span class="n">interval</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">set_interval</span><span class="p">(</span><span class="n">delay</span><span class="p">)</span>
    <span class="no">Thread</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span>
        <span class="kp">loop</span> <span class="k">do</span>
            <span class="nb">sleep</span> <span class="n">delay</span>
            <span class="k">yield</span>
        <span class="k">end</span>
    <span class="k">end</span>
<span class="k">end</span>

<span class="n">sunrise</span> <span class="o">=</span> <span class="n">set_interval</span><span class="p">(</span><span class="n">interval</span><span class="p">)</span> <span class="k">do</span>
    <span class="k">if</span> <span class="n">luminence</span> <span class="o">&gt;=</span> <span class="mi">1</span>
        <span class="n">sunrise</span><span class="o">.</span><span class="n">kill</span>
    <span class="k">end</span>

    <span class="n">light</span><span class="o">.</span><span class="n">set_color</span><span class="p">(</span><span class="ss">LIFX</span><span class="p">:</span><span class="ss">:Color</span><span class="o">.</span><span class="n">hsl</span><span class="p">(</span><span class="n">hue</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">luminence</span><span class="p">),</span> <span class="ss">duration</span><span class="p">:</span> <span class="n">interval</span><span class="p">)</span>
    <span class="n">luminence</span> <span class="o">=</span> <span class="n">luminence</span> <span class="o">+</span> <span class="n">luminence_increment</span>
    <span class="n">hue</span> <span class="o">=</span> <span class="n">hue</span> <span class="o">+</span> <span class="n">hue_increment</span>
<span class="k">end</span>

<span class="n">sunrise</span><span class="o">.</span><span class="n">join</span>
</pre>
</div>
<p>The code is pretty simple I setup a thread to run every 6 seconds and slowly increase the hue and luminance values over a 2 minute period.</p>
<p>The Lifx gem API is pretty simple I would suggest going over to <a href="http://rubydoc.info/github/lifx/lifx-gem/master/frames">their docs</a> to learn more about specifics. Basically my code is just discovering my one and only light that has the label that I passed in as an argument and then I just increment the HSL colour values.</p>
<h2>Hooking up the cronjob</h2>
<p>On the server side of things in my raspberry pi I register this shell script to run.</p>
<div class="syntax code">
<pre><span class="c">#!/bin/bash</span>

<span class="nb">source</span> ~/.rvm/scripts/rvm
<span class="nb">cd</span> ~/sunrise-lifx
bundle <span class="nb">exec </span>ruby ~/sunrise-lifx/sunrise.rb <span class="s2">"Bedroom"</span>
</pre>
</div>
<p>The reason I do this is because I need to source in the rvm scripts so I can run my ruby code. Cron starts a bare bones shell so even if you already source in stuff in a .bash_profile you&#8217;ll still need to manually source in the stuff you need when executing as a cron job.</p>
<p>The cronjob runs two scripts, one to trigger the sunrise simulation on weekdays at 6:28am and another that runs 15 minutes later to turn the light off as I should be up by then.</p>
<div class="syntax code">
<pre>28 6 * * 1-5 ~/sunrise-lifx/sunrise.sh &gt;&gt; ~/sunrise-lifx/cronOutput.txt 2&gt;&amp;1
45 6 * * 1-5 ~/sunrise-lifx/turn-off.sh &gt;&gt; ~/sunrise-lifx/cronOutput.txt 2&gt;&amp;1
</pre>
</div>
<p>That&#8217;s it. Super simple setup and seems to work pretty well so far.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/hacking/sunrise-lifx/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>A follow up to obtaining 60fps scrolling performance</title>
		<link>https://www.thecssninja.com/css/follow-up-60fps-scroll</link>
		<comments>https://www.thecssninja.com/css/follow-up-60fps-scroll#comments</comments>
		<pubDate>Mon, 13 Jan 2014 12:02:16 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1342</guid>
		<description><![CDATA[60fps scrolling article got quite a few people excited but also a few smart people to point out some flaws in the approach. I was planning to just update the original article but I&#8217;ve compiled so much info about this topic that I thought it would be better to do a follow up article and &#8230; <a href="https://www.thecssninja.com/css/follow-up-60fps-scroll" class="more-link">Continue reading<span class="screen-reader-text"> "A follow up to obtaining 60fps scrolling performance"</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.thecssninja.com/javascript/pointer-events-60fps">60fps scrolling article</a> got quite a few people excited but also a few smart people to point out some flaws in the approach. I was planning to just update the original article but I&#8217;ve compiled so much info about this topic that I thought it would be better to do a follow up article and dive into the detail of the techniques that I tried and some solutions to the critiques made about the last technique.<span id="more-1342"></span></p>
<p>Take a look at the <a href="http://jsbin.com/oqUqIxU/1/quiet" target="_blank">final test case</a> that performs the best, watch the <a href="http://www.youtube.com/watch?v=MFY8UX3P-LQ" target="_blank">screencast</a> and checkout the <a href="https://github.com/ryanseddon/60fps-scroll" target="_blank">60fps-scroll repo</a>. Read on to learn why.</p>
<h2>Critiques</h2>
<p>There were two valid critiques that were brought up:</p>
<ul>
<li>Style recalculation hit when adding class to body.</li>
<li>User clicking something while scrolling or before timeout runs.</li>
</ul>
<h2>Pointer-events: none not the silver bullet</h2>
<p>The original idea of disabling <code>pointer-events</code> on the body gives you the illusion that scroll performance is great but let&#8217;s face it simple sites don&#8217;t really have scroll jank issues. Where we do see flaws in the original technique is when we it&#8217;s used on more complicated visually rich sites. By adding a class to the body we force the browser to do a style recalculation which is costly on the body as it has to traverse the whole tree to make sure this top level class doesn&#8217;t effect any children.</p>
<p><img src="http://www.thecssninja.com/wp-content/uploads/2014/01/recalc-hit.png" alt="Style recalculation hit on more complex DOM structures" width="609" height="173" class="article-img main-img" /></p>
<p>The above image is a timeline snapshot of the more complex DOM using the older technique of changing the class on the body, as you can see this can just blow away your pursuit of 60fps scrolling before you&#8217;ve even began.</p>
<h2>Mobile</h2>
<p>On mobile or touch based devices this technique really has no effect as this is only relevant for mouse cursors. Running the test case in an iPhone works as normal when scrolling.</p>
<h2>Firefox degradation</h2>
<p>The original test case was very janky in Firefox and quite slow. Without the equivalent timeline tool it&#8217;s hard to say what the browser is exactly doing on scroll. The revised technique performs a lot better as it doesn&#8217;t trigger any repaints or high style recalculations.</p>
<h2>What other browsers do on scroll</h2>
<p><a href="http://www.thecssninja.com/javascript/pointer-events-60fps/comment-page-1#comment-7528" target="_blank">IE11</a> and <a href="http://www.thecssninja.com/javascript/pointer-events-60fps/comment-page-1#comment-7562" target="_blank">Firefox</a> both turn off hit testing on scroll if the users mouse doesn&#8217;t move. Safari I&#8217;m not sure what it does.</p>
<h2>Solving clicks before the timeout has triggered</h2>
<p>One of the critiques was what if the user clicks on something as they&#8217;re scrolling or clicks something before the timeout has ran once the user stops scrolling.</p>
<p>We can semi-solve this via the use of <code>elementFromPoint</code> and triggering synthetic events. This won&#8217;t solve every edge case but it will get you mostly there.</p>
<div class="syntax code">
<pre><span class="c1">// capture all clicks and store x, y coords for later</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="o">===</span> <span class="nx">cover</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">event</span><span class="p">.</span><span class="nx">synthetic</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">pos</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">clientX</span><span class="p">;</span>
      <span class="nx">pos</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">clientY</span><span class="p">;</span>
      <span class="nx">clicked</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">dispatchClick</span><span class="p">(</span><span class="nx">coords</span><span class="p">){</span>
  <span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s2">"MouseEvent"</span><span class="p">),</span>
      <span class="nx">elem</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">elementFromPoint</span><span class="p">(</span><span class="nx">coords</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">coords</span><span class="p">.</span><span class="nx">y</span><span class="p">);</span>

  <span class="nx">event</span><span class="p">.</span><span class="nx">initMouseEvent</span><span class="p">(</span>
      <span class="s2">"click"</span><span class="p">,</span>
      <span class="kc">true</span> <span class="cm">/* bubble */</span><span class="p">,</span> <span class="kc">true</span> <span class="cm">/* cancelable */</span><span class="p">,</span>
      <span class="nb">window</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span>
      <span class="nx">coords</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">coords</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="cm">/* coordinates */</span>
      <span class="kc">false</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="cm">/* modifier keys */</span>
      <span class="mi">0</span> <span class="cm">/*left*/</span><span class="p">,</span> <span class="kc">null</span>
  <span class="p">);</span>
  <span class="nx">event</span><span class="p">.</span><span class="nx">synthetic</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

  <span class="nx">elem</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
<span class="p">}</span>
</pre>
</div>
<p>By attaching a listener to the document we can capture all clicks that happen before the timeout fires by storing the cursor x, y coordinates when the timeout fires we can target the element via elementFromPoint and then trigger a synthetic event on that element.</p>
<div class="syntax code">
<pre><span class="c1">// On timeout check to make sure the user actually clicked during scroll</span>
<span class="c1">// Dispatch synthetic click event</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
  <span class="c1">//...</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">clicked</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">dispatchClick</span><span class="p">(</span><span class="nx">pos</span><span class="p">);</span>
    <span class="nx">clicked</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">},</span><span class="mi">500</span><span class="p">);</span>
</pre>
</div>
<p>The downside to this approach is this will only trigger listeners attached via JavaScript it won&#8217;t trigger an actual click so if I was to click on a form submit button it wouldn&#8217;t do anything. This won&#8217;t work on iframes either so things like social button embeds etc. Of course once the timeout has fired all clicks will worked as expected.</p>
<h2>Alternate solution using a div that follows the mouse cursor</h2>
<p>One suggestion in the <a href="http://www.thecssninja.com/javascript/pointer-events-60fps/comment-page-1#comment-7469" target="_blank">comments</a> was to use a small cover div that follows the mouse cursor as the user scrolls and use CSS transforms to move it on the viewport so it won&#8217;t cause any layouts. I found the performance degraded compared to the original <code>pointer-events</code> technique and it also oddly triggered layouts/paints when changing the translate property but not all the time. Perhaps my <a href="http://jsbin.com/IdIhacE/2/quiet" target="_blank">test case</a> is flawed? If you know how to fix it let me know.</p>
<p>Check out the <a href="http://jsbin.com/IdIhacE/2/quiet" target="_blank">test case</a> for yourself. Note that this demo only alters the <code>webkitTransform</code> property so it won&#8217;t do anything in Firefox.</p>
<h2>Using a cover div + pointer-events: none</h2>
<p>We&#8217;ll dive into the following test case that uses a cover div and <code>pointer-events: none</code> and go through some optimisations to improve it further.</p>
<p><iframe width="640" height="480" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/MFY8UX3P-LQ" class="iframe-fix"></iframe></p>
<p>One of the main things when working with lots of images is not resizing them through CSS or the img elements width/height attributes as the browser has to decode the image and resize it before painting it to the screen. This makes sure your scrolling performance isn&#8217;t hit hard by big repaints. Harder to do when catering for High DPI screens but definitely a solution that the <a href="http://picture.responsiveimages.org/" target="_blank">picture element</a> will solve in the future.</p>
<h2>60fps-scroll library</h2>
<p>I created a <a href="https://github.com/ryanseddon/60fps-scroll" target="_blank">little library</a> that wraps up this technique that you can drop into your site and see if it helps your scroll performance.</p>
<h2>Pursuit of a false metric?</h2>
<p>Some may have the feeling that this may be a case of the premature optimisation gremlins and they&#8217;re right. I wouldn&#8217;t get caught up on 60fps performance until you have a problem with your scrolling where it can really effect the end users experience within your site.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/css/follow-up-60fps-scroll/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>60fps scrolling using pointer-events: none</title>
		<link>https://www.thecssninja.com/css/pointer-events-60fps</link>
		<comments>https://www.thecssninja.com/css/pointer-events-60fps#comments</comments>
		<pubDate>Mon, 25 Nov 2013 10:30:36 +0000</pubDate>
		<dc:creator><![CDATA[Ryan Seddon]]></dc:creator>
				<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.thecssninja.com/?p=1304</guid>
		<description><![CDATA[Paul Lewis did an interesting article a while back about avoiding unnecessary paints through disabling hover effects as the user scrolls, which is a great approach. The down side being managing all your hover states through a parent class. UPDATE: I&#8217;ve done a follow up article which demonstrates a more robust technique. .hover .element:hover { &#8230; <a href="https://www.thecssninja.com/css/pointer-events-60fps" class="more-link">Continue reading<span class="screen-reader-text"> "60fps scrolling using pointer-events: none"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Paul Lewis did an interesting article a while back about <a href="http://www.html5rocks.com/en/tutorials/speed/unnecessary-paints/">avoiding unnecessary paints</a> through disabling hover effects as the user scrolls, which is a great approach. The down side being managing all your hover states through a parent class.<span id="more-1304"></span></p>
<p><strong>UPDATE</strong>: I&#8217;ve done a <a href="http://www.thecssninja.com/javascript/follow-up-60fps-scroll">follow up article</a> which demonstrates a more robust technique.</p>
<div class="syntax code">
<pre><span class="nc">.hover</span> <span class="nc">.element</span><span class="nd">:hover</span> <span class="p">{</span>
  <span class="n">box</span><span class="o">-</span><span class="n">shadow</span><span class="o">:</span> <span class="m">1px</span> <span class="m">1px</span> <span class="m">1px</span> <span class="m">#000</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p>That approach doesn&#8217;t scale well and it creates unnecessary specificity in your CSS.</p>
<p>The gist of the technique is on scrolling the <code>.hover</code> class is removed from the body and all your <code>.hover</code> selectors won&#8217;t match until the user stops scrolling and the class is then added back to the body. </p>
<p>Then I saw a genius little tweet from Christian Schaefer.</p>
<blockquote class="twitter-tweet" lang="en"><p><a href="https://twitter.com/paul_irish">@paul_irish</a> Easy. Apply &quot;pointer-events: none&quot; to the &lt;body&gt; on scrollstart and remove it on scrollend. <a href="https://twitter.com/tabatkins">@tabatkins</a></p>
<p>&mdash; Christian Schaefer (@derSchepp) <a href="https://twitter.com/derSchepp/statuses/400394164350644224">November 12, 2013</a></p></blockquote>
<p><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p>
<h2>Pointer-events property to the rescue</h2>
<p>This is a much better approach as it will just make the mouse pass through the element that has the <code>pointer-events: none</code> property set. Take a look at the screencast I did showing the dramatic difference disabling hover makes.</p>
<p><iframe width="640" height="480" class="iframe-fix" src="//www.youtube.com/embed/IyHb0SJms6w" frameborder="0" allowfullscreen></iframe></p>
<p>We get all the benefits of the original approach without introducing maintainability and specificity issues in our CSS.</p>
<div class="syntax code">
<pre><span class="nc">.disable-hover</span> <span class="p">{</span>
  <span class="k">pointer</span><span class="o">-</span><span class="n">events</span><span class="o">:</span> <span class="k">none</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p>All we have to do is add the <code>.disable-hover</code> class to the body when the user begins to scroll. This then allows the users cursor to pass through the body and thus disable any hover effects.</p>
<div class="syntax code">
<pre><span class="kd">var</span> <span class="nx">body</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">,</span>
    <span class="nx">timer</span><span class="p">;</span>

<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'scroll'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">body</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">contains</span><span class="p">(</span><span class="s1">'disable-hover'</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">body</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">'disable-hover'</span><span class="p">)</span>
  <span class="p">}</span>
  
  <span class="nx">timer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
    <span class="nx">body</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="s1">'disable-hover'</span><span class="p">)</span>
  <span class="p">},</span><span class="mi">500</span><span class="p">);</span>
<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
</pre>
</div>
<p>The code is pretty simple we clear the timeout, this is important after the initial scroll, check to see if the disable class isn&#8217;t already on the body element and then set up a delayed timer to remove the class once the user has stopped scrolling for at least 500ms.</p>
<h2>A more robust technique</h2>
<p>Now applying pointer-events to the body will work just fine in most cases but if pointer-events: auto is applied to any child elements they will override the parent property and cause janky scroll. </p>
<div class="syntax code">
<pre><span class="nc">.disable-hover</span><span class="o">,</span>
<span class="nc">.disable-hover</span> <span class="o">*</span> <span class="p">{</span>
  <span class="k">pointer</span><span class="o">-</span><span class="n">events</span><span class="o">:</span> <span class="k">none</span> <span class="cp">!important</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p>Easy solution is to do the asterisk selector and add an important to the property value so it will disable any child elements with pointer-events turned on.</p>
<p>Take a look at the <a href="http://jsbin.com/oNiVUYe/5/quiet">testcase</a> for yourself and run some timelines to see the performance gains from this simple technique.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.thecssninja.com/css/pointer-events-60fps/feed</wfw:commentRss>
		<slash:comments>45</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.301 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2020-04-01 16:29:32 -->
