<![CDATA[Osvaldas Valutis blog]]>http://github.com/dylang/node-rssGatsbyJSFri, 26 Jan 2024 11:42:20 GMT<![CDATA[Moving the first JavaScript array item to the end]]>https://osvaldas.info/moving-the-first-js-array-item-to-the-endhttps://osvaldas.info/moving-the-first-js-array-item-to-the-endSun, 24 Dec 2023 00:00:00 GMT<p>As an example for the use case, have you every ran into API’s which proudly feed you with week day based data, but ordered not in a way you expected, typically starting the week with Sunday, not Monday, or the other way around?</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Sunday</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Monday</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Saturday</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">]</span></code></pre></div> <p>Well, there’s a single line cure for this:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"> data<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre></div> <p>Result:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token comment">// -> [`Monday`, ..., `Saturday`, `Sunday`]</span></code></pre></div> <p>Keep in mind you don’t need to reassign this to the variable as the modifications are performed directly on the array.</p><![CDATA[The Great Gatsby Redesign]]>https://osvaldas.info/the-great-gatsby-redesignhttps://osvaldas.info/the-great-gatsby-redesignSat, 21 Jan 2023 00:00:00 GMT<p>Reading my <a href="/redesign">previous redesign post</a> from 2014 I cannot believe how significantly the industry has evolved since then: no more Modernizr, loadCSS, custom responsive image implementation; IE out of image for good, Edge and Opera adapting Blink, Jamstack and that’s just a drop in the ocean. Most of these “news” feel ancient already.</p> <p>The latter fancy word is what I turned to this time, or to be more specific – <a href="https://www.gatsbyjs.com" target="_blank" rel="nofollow noopener noreferrer">Gatsby</a> deployed on <a href="https://www.netlify.com" target="_blank" rel="nofollow noopener noreferrer">Netlify</a> meaning I switched PHP for Node, SQL for GraphQL, CodeIgniter for Gatsby, vanilla HTML, CSS and JavaScript for React and Styled Components, custom made CMS for Markdown and static site generator.</p> <p>We’ve been using Gatsby at <a href="https://www.oddcamp.com" target="_blank" rel="nofollow noopener noreferrer">Odd Camp</a> for a few years now, starting from its very first version, so this felt like a natural choice for me (we’ve got our own <a href="https://github.com/oddcamp/gatsby-starter-oddcamp" target="_blank" rel="nofollow noopener noreferrer">Gatsby starter</a>). Oh, and there some quality plugins to choose from. I’ve implemented the following:</p> <ul> <li>gatsby-source-filesystem</li> <li>gatsby-plugin-feed</li> <li>gatsby-transformer-remark</li> <li>gatsby-remark-copy-linked-files</li> <li>gatsby-remark-extract-image-attributes</li> <li>gatsby-remark-images</li> <li>gatsby-remark-images-insert-wrapper-attributes</li> <li>gatsby-remark-embed-video</li> <li>gatsby-remark-http-to-https</li> <li>gatsby-remark-external-links</li> <li>gatsby-remark-prismjs</li> <li>gatsby-remark-autolink-headers</li> <li>gatsby-plugin-catch-links</li> <li>gatsby-plugin-svgr</li> <li>gatsby-plugin-styled-components</li> <li>gatsby-plugin-sitemap</li> <li>gatsby-plugin-offline</li> <li>gatsby-plugin-eslint</li> </ul> <p>Even though we have slowly started replacing Gatsby with <a href="https://nextjs.org" target="_blank" rel="nofollow noopener noreferrer">Next.js</a> I do have some Gatsby techniques I am going to share.</p> <p>The previous version of my website is archived at <a href="https://v3.osvaldas.info" target="_blank" rel="nofollow noopener noreferrer">v3.osvaldas.info</a>.</p><![CDATA[Container-Adapting Tabs With “More” Button]]>https://osvaldas.info/container-adapting-tabs-with-more-buttonhttps://osvaldas.info/container-adapting-tabs-with-more-buttonWed, 02 May 2018 00:00:00 GMT<p>Or the priority navigation pattern, or progressively collapsing navigation menu. We can name it in at least three ways..</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/container-adapting-tabs-with-more-button.jpg" data-alt="Container-Adapting Tabs With &quot;More&quot; Button"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 45.8128078817734%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIA/9oADAMBAAIQAxAAAAGzhpAaGR//xAAbEAACAQUAAAAAAAAAAAAAAAAAAgEDEBETMf/aAAgBAQABBQJlyVtiEcv/AP/EABcRAAMBAAAAAAAAAAAAAAAAAAECEBH/2gAIAQMBAT8BDZP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAaEAEAAQUAAAAAAAAAAAAAAAABAgASIDJx/9oACAEBAAY/Atk5RbKTj//EABkQAAIDAQAAAAAAAAAAAAAAAAERABBRMf/aAAgBAQABPyEiqMZwdDHODyhX/9oADAMBAAIAAwAAABCUH//EABcRAAMBAAAAAAAAAAAAAAAAAAEQESH/2gAIAQMBAT8Qt0Vf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAEEAwAAAAAAAAAAAAAAAREAEDGRIUFh/9oACAEBAAE/EElGeOdlEbVVhBMYKSrlCdW7W//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/4fb6c60a8d39659dcefad22a47f21a5d/3466e/container-adapting-tabs-with-more-button.webp 203w, /static/4fb6c60a8d39659dcefad22a47f21a5d/d7d9a/container-adapting-tabs-with-more-button.webp 405w, /static/4fb6c60a8d39659dcefad22a47f21a5d/6da5f/container-adapting-tabs-with-more-button.webp 810w, /static/4fb6c60a8d39659dcefad22a47f21a5d/1eaf0/container-adapting-tabs-with-more-button.webp 1215w, /static/4fb6c60a8d39659dcefad22a47f21a5d/08048/container-adapting-tabs-with-more-button.webp 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/4fb6c60a8d39659dcefad22a47f21a5d/37414/container-adapting-tabs-with-more-button.jpg 203w, /static/4fb6c60a8d39659dcefad22a47f21a5d/32fb1/container-adapting-tabs-with-more-button.jpg 405w, /static/4fb6c60a8d39659dcefad22a47f21a5d/92b2a/container-adapting-tabs-with-more-button.jpg 810w, /static/4fb6c60a8d39659dcefad22a47f21a5d/290c6/container-adapting-tabs-with-more-button.jpg 1215w, /static/4fb6c60a8d39659dcefad22a47f21a5d/5814a/container-adapting-tabs-with-more-button.jpg 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/4fb6c60a8d39659dcefad22a47f21a5d/92b2a/container-adapting-tabs-with-more-button.jpg" alt="Container-Adapting Tabs With &quot;More&quot; Button" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>There are multiple UX solutions for tabs and menus and each of them have their own advantages over another, you just need to pick the best for the case you are trying to solve. At design and development agency <a href="https://www.oddcamp.com" target="_blank" rel="nofollow noopener noreferrer">Odd Camp</a> we were debating on the most appropriate UX technique for tabs for our client’s website…</p> <p>I wrote an article, coded a demo and got it all published on CSS-Tricks — you're very welcome to read, try and use it!</p> <ul> <li><a href="https://css-tricks.com/container-adapting-tabs-with-more-button/" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a></li> <li><a href="https://codepen.io/osvaldas/pen/xWNLXy" target="_blank" rel="nofollow noopener noreferrer"><strong>Try the demo</strong></a></li> </ul><![CDATA[I Joined Odd Camp – Come Hire Us!]]>https://osvaldas.info/i-joined-odd-camp-come-hire-ushttps://osvaldas.info/i-joined-odd-camp-come-hire-usFri, 06 Oct 2017 00:00:00 GMT<p>Actually I did that right before entering the year of 2017 and my official start was on January 1st. After getting back to freelancing in 2012 I felt a need again to join a team and thus gain more from my professional life. As the matter of fact during the first month at my new workplace I felt like I learned more than in a whole year of freelancing. I’m not saying freelancing is wrong. It’s good as long as: you are able to take out most of it; it doesn’t isolate you; it doesn’t become a routine suppressing your progress.</p> <p>Therefore I couldn’t be more happy to have my road crossed with <a href="https://www.oddcamp.com" target="_blank" rel="nofollow noopener noreferrer">Odd Camp</a> – a team of talented and highly experienced designers and developers. Supercharged with a great sense of humor we design and develop digital products and services for clients in Sweden and around the globe. We are a small team of 10 with the heart in Stockholm, but at the same time distributed accross the world: Sweden, Portugal, Greece, Slovakia, Bulgaria, Dominican Republic and Lithuania.</p> <p>2017 was a blast. Besides getting my hands some fancy web technologies on and interesting client projects, we also had a one week long company trip in Nice (France) and its surroundings. Then a week later the most of our front-end division attended <a href="https://www.mirrorconf.com" target="_blank" rel="nofollow noopener noreferrer">Mirror Conference</a> in Braga (Portugal). As if this was not enough, during our conference visit we gave a warm and humble speech called “Designing the Remote Business” at Braga.Design.JS meetup:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/i-joined-odd-camp-come-hire-us.jpg" data-alt="Odd Camp at Braga.Design.JS meetup"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 56.15763546798029%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQID/9oADAMBAAIQAxAAAAGTF1sbeZ4j/8QAGhABAAIDAQAAAAAAAAAAAAAAAQACAxESE//aAAgBAQABBQKthOhizR72AyT/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAEAAgMAAAAAAAAAAAAAAAABEBEAIVH/2gAIAQEABj8COyFayiP/xAAYEAADAQEAAAAAAAAAAAAAAAAAAREhQf/aAAgBAQABPyHt4TyVl6IuMtOw1FhNEWjen//aAAwDAQACAAMAAAAQ9D//xAAXEQADAQAAAAAAAAAAAAAAAAAAASER/9oACAEDAQE/EFYYf//EABYRAAMAAAAAAAAAAAAAAAAAABAhMf/aAAgBAgEBPxCMf//EABsQAQADAAMBAAAAAAAAAAAAAAEAESExYYHw/9oACAEBAAE/EDPb6wtM1gRQCiM+2NdKGcywhSAogdRjQWh5GbLaz//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/adffe1962a76b7f9da97f6b7efa4fe98/3466e/i-joined-odd-camp-come-hire-us.webp 203w, /static/adffe1962a76b7f9da97f6b7efa4fe98/d7d9a/i-joined-odd-camp-come-hire-us.webp 405w, /static/adffe1962a76b7f9da97f6b7efa4fe98/6da5f/i-joined-odd-camp-come-hire-us.webp 810w, /static/adffe1962a76b7f9da97f6b7efa4fe98/1eaf0/i-joined-odd-camp-come-hire-us.webp 1215w, /static/adffe1962a76b7f9da97f6b7efa4fe98/77fde/i-joined-odd-camp-come-hire-us.webp 1620w, /static/adffe1962a76b7f9da97f6b7efa4fe98/96d48/i-joined-odd-camp-come-hire-us.webp 2048w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/adffe1962a76b7f9da97f6b7efa4fe98/37414/i-joined-odd-camp-come-hire-us.jpg 203w, /static/adffe1962a76b7f9da97f6b7efa4fe98/32fb1/i-joined-odd-camp-come-hire-us.jpg 405w, /static/adffe1962a76b7f9da97f6b7efa4fe98/92b2a/i-joined-odd-camp-come-hire-us.jpg 810w, /static/adffe1962a76b7f9da97f6b7efa4fe98/290c6/i-joined-odd-camp-come-hire-us.jpg 1215w, /static/adffe1962a76b7f9da97f6b7efa4fe98/ddd99/i-joined-odd-camp-come-hire-us.jpg 1620w, /static/adffe1962a76b7f9da97f6b7efa4fe98/09658/i-joined-odd-camp-come-hire-us.jpg 2048w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/adffe1962a76b7f9da97f6b7efa4fe98/92b2a/i-joined-odd-camp-come-hire-us.jpg" alt="Odd Camp at Braga.Design.JS meetup" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>In case you’ve been looking for a decent team to trust the design and technical parts of your online presence or business, don’t you ever hesitate to <a href="https://www.oddcamp.com" target="_blank" rel="nofollow noopener noreferrer">contact Odd Camp</a>.</p><![CDATA[Service Worker for Middleman based websites]]>https://osvaldas.info/service-worker-for-middleman-based-websiteshttps://osvaldas.info/service-worker-for-middleman-based-websitesSun, 24 Sep 2017 00:00:00 GMT<p><a href="https://middlemanapp.com" target="_blank" rel="nofollow noopener noreferrer">Middleman</a> is a Ruby based static site generator which we use heavily both for prototyping (checkout our <a href="https://github.com/kollegorna/middleman-boilerplate" target="_blank" rel="nofollow noopener noreferrer">Middleman boilerplate</a>) and production sites. In my previous article on <a href="/service-worker-gotchas">Service Worker</a>, I overviewed the most common challenges you may face when implementing the technology. This time I'd like to dive into a single specific topic of enabling a worker on Middleman based website as there are a few things to deal with…</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-for-middleman-based-websites.jpg" data-alt="Service Worker for Middleman based websites"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 30.541871921182263%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAQF/9oADAMBAAIQAxAAAAHRpnUAf//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABUQAQEAAAAAAAAAAAAAAAAAABBR/9oACAEBAAE/IY//2gAMAwEAAgADAAAAEIwf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABkQAQACAwAAAAAAAAAAAAAAAAERUQAQYf/aAAgBAQABPxBFYMAi9KyStf/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/a2b95b413c253f25edbbccf013dfe532/3466e/service-worker-for-middleman-based-websites.webp 203w, /static/a2b95b413c253f25edbbccf013dfe532/d7d9a/service-worker-for-middleman-based-websites.webp 405w, /static/a2b95b413c253f25edbbccf013dfe532/6da5f/service-worker-for-middleman-based-websites.webp 810w, /static/a2b95b413c253f25edbbccf013dfe532/1eaf0/service-worker-for-middleman-based-websites.webp 1215w, /static/a2b95b413c253f25edbbccf013dfe532/77fde/service-worker-for-middleman-based-websites.webp 1620w, /static/a2b95b413c253f25edbbccf013dfe532/0f190/service-worker-for-middleman-based-websites.webp 1800w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/a2b95b413c253f25edbbccf013dfe532/37414/service-worker-for-middleman-based-websites.jpg 203w, /static/a2b95b413c253f25edbbccf013dfe532/32fb1/service-worker-for-middleman-based-websites.jpg 405w, /static/a2b95b413c253f25edbbccf013dfe532/92b2a/service-worker-for-middleman-based-websites.jpg 810w, /static/a2b95b413c253f25edbbccf013dfe532/290c6/service-worker-for-middleman-based-websites.jpg 1215w, /static/a2b95b413c253f25edbbccf013dfe532/ddd99/service-worker-for-middleman-based-websites.jpg 1620w, /static/a2b95b413c253f25edbbccf013dfe532/251e3/service-worker-for-middleman-based-websites.jpg 1800w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/a2b95b413c253f25edbbccf013dfe532/92b2a/service-worker-for-middleman-based-websites.jpg" alt="Service Worker for Middleman based websites" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="versioning" style="position:relative;"><a href="#versioning" aria-label="versioning permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Versioning</h2> <p>Service Worker is all about <a href="/service-worker-gotchas#service-worker-strategies">caching resources</a>. In order to invalidate cache with a worker, you have to assign a version number to each resource cached. You could do that manually each time you make a change on a website, but that's probably not a thing you want to keep in mind and spend the time on. So how can we automate the process on the Middleman setup?</p> <p>Since Middleman is a static site generator, one of the final phases of serving a website is making a build which, simply put, is converting source files into static ones. Adding <code class="language-text">.erb</code> extension to a static file enables us to execute Ruby commands in it. This basically means that we can insert a dynamic content areas in the file and so make them differ on each build. Which is exactly what we need for implementing versioning for our Service Worker and thus instantly serve the fresh content for users.</p> <p>The worker file ought to be placed here:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">/source/serviceworker.js.erb</code></pre></div> <p>I found Unix timestamp to be a perfect method for naming the resource versions as in principle they are always unique on each build. Now we can put this in <code class="language-text">serviceworker.js.erb</code>:</p> <div class="gatsby-highlight" data-language="rb"><pre class="language-rb"><code class="language-rb">const version <span class="token operator">=</span> <span class="token string-literal"><span class="token string">'&lt;%= Time.now.to_i %>'</span></span><span class="token punctuation">;</span></code></pre></div> <p>…and take the advantage of <code class="language-text">version</code> further in the code.</p> <h2 id="digested-assets" style="position:relative;"><a href="#digested-assets" aria-label="digested assets permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Digested assets</h2> <p>Digesting the assets is good for cache busting. This makes the change of your border radius instantly visible for users. Therefore, I wouldn't be surprised if you have <code class="language-text">:asset_hash</code> activated. However, that leads to a problem: hardcoding paths of the assets won't work because they differ on each build (the files get renamed to be more precise). But because of <code class="language-text">.erb</code>, we can insert dynamic paths in Ruby way:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> criticalResources <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'&lt;%= asset_path :css, "/#{app.config[:css_dir]}/application" %>'</span><span class="token punctuation">,</span> <span class="token string">'&lt;%= asset_path :js, "/#{app.config[:js_dir]}/application" %>'</span><span class="token punctuation">,</span> <span class="token string">'&lt;%= asset_path :images, "logo.svg" %>'</span> <span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre></div> <p>As you might have noticed, CSS and JS implementations differ from images. I did run into a weird issue where <code class="language-text">asset_path</code> were producing wrong paths to CSS and JS files. This tiny workaround solved this.</p> <h2 id="multiple-languages" style="position:relative;"><a href="#multiple-languages" aria-label="multiple languages permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Multiple languages</h2> <p>The number of languages on a website is a thing that you do not alter very often. But if you decide to hardcode the list in worker's file, there is a big chance you'll forget to update it when the time comes. Again, that's probably not the thing you want to keep in mind, so it's best to automate stuff when possible. Luckily, there is a way to get a list of languages available in Middleman and they can be inserted like this:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> appLangs <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'&lt;%= app.extensions[:i18n].options.langs.join("'</span><span class="token punctuation">,</span><span class="token string">'") %>'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre></div> <p>Using <code class="language-text">appLangs</code> you can automatically <a href="/service-worker-gotchas#service-worker-for-multilingual-website">cache these front pages and serve the appropriate "offline" page/image</a>.</p> <h2 id="debugging" style="position:relative;"><a href="#debugging" aria-label="debugging permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Debugging</h2> <p>Service Worker is a test demanding technology and console logging is probably the most important factor in testing. But the thing is that you likely don't want to log these messages into user's browser and only want to do that in development mode. Middleman provides means to detect the environment type for an app:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">logMsg</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">msg</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'&lt;%= app.config[:environment] %>'</span> <span class="token operator">==</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> <span class="token function">logMsg</span><span class="token punctuation">(</span><span class="token string">'Install event'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> <span class="token function">logMsg</span><span class="token punctuation">(</span><span class="token string">'Activate event'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// etc.</span></code></pre></div> <p>By the way, there is more to <a href="/service-worker-gotchas#debugging-service-worker">debugging Service Worker</a>.</p><![CDATA[Service Worker gotchas]]>https://osvaldas.info/service-worker-gotchashttps://osvaldas.info/service-worker-gotchasThu, 03 Aug 2017 00:00:00 GMT<p><a href="https://developers.google.com/web/fundamentals/getting-started/primers/service-workers" target="_blank" rel="nofollow noopener noreferrer">Service Worker</a> has already been here for a while: since 2015–09 it has been fully supported in Chrome/Opera and if compared to what we have today it has gone a promising way of improvements, bug fixes, became more easily debuggable and is supported much widely (hello Firefox). That led us into using the technology in production and implementing it in our kollegorna.se website, as well as some client projects. We've learned there quite a few gotchas to grasp in order to get Service Worker working <em>correctly…</em></p> <p>Here is the list of what I'll be overviewing in the article:</p> <ul> <li><a href="#service-worker-is-a-part-of-progressive-web-apps">Service Worker is a part of Progressive Web Apps</a></li> <li><a href="#what-service-worker-is-for">What Service Worker is for</a></li> <li><a href="#registering-a-service-worker">Registering a Service Worker</a></li> <li><a href="#https-and-localhost">HTTPS and localhost</a></li> <li><a href="#service-worker-working-scope">Service Worker working scope</a></li> <li><a href="#es6-to-be-or-not-to-be">ES6: to be or not to be?</a></li> <li><a href="#offline-page">"Offline" page</a></li> <li><a href="#service-worker-lifecycle-and-events-hierarchy">Service Worker lifecycle and events hierarchy</a></li> <li><a href="#critical-and-non-critical-resources">Critical and non-critical resources</a></li> <li><a href="#service-worker-strategies">Service Worker strategies</a></li> <li><a href="#serving-offline-image">Serving "offline" image</a></li> <li><a href="#garbage-in-cache-is-your-problem">Garbage in cache is your problem</a></li> <li><a href="#service-worker-and-dom">Service Worker and DOM</a></li> <li><a href="#service-worker-for-multilingual-website">Service Worker for multilingual website</a></li> <li><a href="#service-worker-is-backend-dependent">Service Worker is backend-dependent</a></li> <li><a href="#debugging-service-worker">Debugging Service Worker</a></li> </ul> <h2 id="service-worker-is-a-part-of-progressive-webapps" style="position:relative;"><a href="#service-worker-is-a-part-of-progressive-webapps" aria-label="service worker is a part of progressive webapps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker is a part of Progressive Web Apps</h2> <p>Service Worker is a part of <a href="https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp" target="_blank" rel="nofollow noopener noreferrer">Progressive Web Apps</a> - a set of means for making websites accessible, functional… and annoying when not used properly (I'm talking to you, Web Push Notifications). The term of Progressive Web Apps was born at Google and is defined by several conceptual terms that you probably already know, but the latest and most exotic ones are: <a href="https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest" target="_blank" rel="nofollow noopener noreferrer">Web App Manifest</a>, <a href="https://developers.google.com/web/fundamentals/engage-and-retain/push-notifications" target="_blank" rel="nofollow noopener noreferrer">Web Push Notifications</a>, <a href="https://developers.google.com/web/fundamentals/engage-and-retain/app-install-banners" target="_blank" rel="nofollow noopener noreferrer">Web App Install Banners</a>.</p> <h2 id="what-service-worker-isfor" style="position:relative;"><a href="#what-service-worker-isfor" aria-label="what service worker isfor permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What Service Worker is for</h2> <p>The best thing about Service Worker is that it enables you to cache static HTML documents, assets and be completely in control of what you are serving to the user. It means that if cached previously your website can be available to the user who has no Internet connection or if your server went down. You can also serve the cached documents and/or assets even if there are no technical issues and that results in much faster website load times. Sounds like fun? With the great power comes responsibility - you'll have to take care of cache size, make sure it's not exceeded and consists of the latest version of assets.</p> <h2 id="registering-a-serviceworker" style="position:relative;"><a href="#registering-a-serviceworker" aria-label="registering a serviceworker permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Registering a Service Worker</h2> <p>Think of Service Worker as a JavaScript file - <code class="language-text">serviceworker.js</code>. The very first step in this journey is to tell the browser your website has a Service Worker that you want to register:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// a check if the technology is supported by browser</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/serviceworker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>It is that simple but here's the first gotcha discovered and documented by <a href="https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/registration" target="_blank" rel="nofollow noopener noreferrer">Jeff from Google</a>:</p> <blockquote> <p>For example, the Google I/O 2016 web app features a short animation before transitioning to the main screen. Our team found that kicking off the service worker registration during the animation could lead to jankiness on low-end mobile devices. Rather than giving users a poor experience, we delayed service worker registration until after the animation, when the browser was most likely to have a few idle seconds.</p> </blockquote> <blockquote> <p>Similarly, if your web app uses a framework that performs additional setup after the page has loaded, look for a framework-specific event that signals when that work is done.</p> </blockquote> <p>Because you can choose when to install Service Worker you should pick the <em>idle</em> moment for it. Here's the rough hypothetical code visualisation of the quote above</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.hero'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'animationend'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// service worker is registered only when the animation ends</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/serviceworker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// ---</span> App<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token comment">// config</span> <span class="token function-variable function">complete</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// service worker is registered only when the app is initiated</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/serviceworker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Anyway, if your website is simpler than that, still the minimum recommendation is registering the worker only when the page has been fully downloaded so that it doesn't slow down the other probably more important processes on your website:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/serviceworker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="https-and-localhost" style="position:relative;"><a href="#https-and-localhost" aria-label="https and localhost permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTTPS and localhost</h2> <p>Another gotcha is that in order to get the Service Worker going your website must be accessible via HTTPS protocol or localhost due to security reasons. The later only serves for development purpose. Despite the fact that Service Worker is operative through localhost it won't work through the IP of your internal network, like 192.160.0.100 or similar.</p> <h2 id="service-worker-workingscope" style="position:relative;"><a href="#service-worker-workingscope" aria-label="service worker workingscope permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker working scope</h2> <p>The location of <code class="language-text">serviceworker.js</code> file on the server is a gotcha as well: the worker will only operate on its current and child paths. In other words if the location of Service Worker file is <code class="language-text">kollegorna.se/serviceworker.js</code>, it will work on the entire site (origin), even if I am browsing here <code class="language-text">kollegorna.se/</code> or there <code class="language-text">kollegorna.se/i/am/here</code>.</p> <p>As you might have realized having the file on <code class="language-text">kollegorna.se/about/serviceworker.js</code> won't invoke the worker when browsing on <code class="language-text">kollegorna.se/</code> nor <code class="language-text">kollegorna.se/contact</code>.</p> <p>Note that domains and subdomains all are different origins, so for example the worker on <code class="language-text">kollegorna.se/serviceworker.js</code> will <em>not</em> operate when browsing on <code class="language-text">labs.kollegorna.se</code>. Placing multiple worker files will get you going.</p> <h2 id="es6-to-be-or-not-tobe" style="position:relative;"><a href="#es6-to-be-or-not-tobe" aria-label="es6 to be or not tobe permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>ES6: to be or not to be?</h2> <p>It is completely safe to use the <em>renewed</em> JavaScript syntax in your <code class="language-text">serviceworker.js</code> file, because browsers which support ES6 syntax also support Service Workers. However, I wouldn't recommend using the new syntax for code that registers Service Worker, unless you use a transpiler. Otherwise JavaScript errors will be thrown for users whose browser won't be able to interpret the new syntax.</p> <p>I will be using ES6 syntax and functions for Service Worker file related code examples here in the article. I encourage you too as it's not the future anymore, it is the present.</p> <h2 id="offline-page" style="position:relative;"><a href="#offline-page" aria-label="offline page permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>“Offline” page</h2> <p>Berry on the cake: besides coding a Service Worker, you should also make a custom "offline" page with its own URL (e.g. <code class="language-text">/offline/</code>) which is a part of Service Worker experience. It will be displayed when a requested page is inaccessible due to internet connection problems or server failure. The how to – later in the article. Our "offline" page at Kollegorna is pretty simple:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/kollegorna-offline-page.jpg?align=overflow" data-alt="Service Worker gotchas" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 83.25123152709361%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEFBgL/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAG95z8GjZwVoAP/xAAaEAACAgMAAAAAAAAAAAAAAAAAAwEUEBEg/9oACAEBAAEFAiN4utLjC83j/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAHBAAAQMFAAAAAAAAAAAAAAAAAAFCkQIQICEx/9oACAEBAAY/AjaWbBymBsYf/8QAGxAAAgIDAQAAAAAAAAAAAAAAAREQIQAgQWH/2gAIAQEAAT8hwiaBdItwzkWvHS//2gAMAwEAAgADAAAAEEDPPP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EAB8QAQACAQMFAAAAAAAAAAAAAAEAESEgMdFRcYGh8f/aAAgBAQABPxAA2I1WSmxOrjHaURajRvl8wvKHzx7n0HOj/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/ad129ded092fa8017733683e52f7e08b/3466e/kollegorna-offline-page.webp 203w, /static/ad129ded092fa8017733683e52f7e08b/d7d9a/kollegorna-offline-page.webp 405w, /static/ad129ded092fa8017733683e52f7e08b/6da5f/kollegorna-offline-page.webp 810w, /static/ad129ded092fa8017733683e52f7e08b/1eaf0/kollegorna-offline-page.webp 1215w, /static/ad129ded092fa8017733683e52f7e08b/e40a7/kollegorna-offline-page.webp 1438w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/ad129ded092fa8017733683e52f7e08b/37414/kollegorna-offline-page.jpg 203w, /static/ad129ded092fa8017733683e52f7e08b/32fb1/kollegorna-offline-page.jpg 405w, /static/ad129ded092fa8017733683e52f7e08b/92b2a/kollegorna-offline-page.jpg 810w, /static/ad129ded092fa8017733683e52f7e08b/290c6/kollegorna-offline-page.jpg 1215w, /static/ad129ded092fa8017733683e52f7e08b/a1e9e/kollegorna-offline-page.jpg 1438w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/ad129ded092fa8017733683e52f7e08b/92b2a/kollegorna-offline-page.jpg" alt="Service Worker gotchas" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="service-worker-lifecycle-and-events-hierarchy" style="position:relative;"><a href="#service-worker-lifecycle-and-events-hierarchy" aria-label="service worker lifecycle and events hierarchy permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker lifecycle and events hierarchy</h2> <p>Each time the user visits your website, the browser downloads the worker's file. If it is a repetitive visit and the file has been downloaded before, the browser compares them both. If there's at least a byte's difference, it's considered there's a new Service Worker available, so it get's registered and the old one is deleted. That is called worker's <em>lifecycle</em>. Le roi est mort! Vive le roi!</p> <p>Once the worker file has been registered, the browser then acts by the instructions defined in that file. Actually the browser triggers few events on different occasions. All we need to do is to catch them and act accordingly, be it to cache a file or respond with a resource. Some events are triggered only once in worker's lifecycle and some - multiple of times. Take a look at the scheme to see who is who:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-1.jpeg?align=overflow" data-alt="Service Worker lifecycle and events hierarchy" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.586206896551722%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAboyCv/EABcQAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQEAAQUC2xip/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Bh//EABgQAAIDAAAAAAAAAAAAAAAAAAAxAREh/9oACAEBAAY/AmM2bP/EABoQAQACAwEAAAAAAAAAAAAAAAEAESFBUbH/2gAIAQEAAT8hCxbnKmF31iAngn//2gAMAwEAAgADAAAAEIwv/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/EF2f/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAR8P/aAAgBAgEBPxDPLBl//8QAGRAAAgMBAAAAAAAAAAAAAAAAAREAITGB/9oACAEBAAE/EA3BxAXYADLFpai0EntJ/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/a3c9e98e1e79f84baec5f4a9a122aa5e/3466e/service-worker-gotchas-1.webp 203w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/d7d9a/service-worker-gotchas-1.webp 405w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/6da5f/service-worker-gotchas-1.webp 810w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/1eaf0/service-worker-gotchas-1.webp 1215w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/64296/service-worker-gotchas-1.webp 1600w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/a3c9e98e1e79f84baec5f4a9a122aa5e/37414/service-worker-gotchas-1.jpg 203w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/32fb1/service-worker-gotchas-1.jpg 405w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/92b2a/service-worker-gotchas-1.jpg 810w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/290c6/service-worker-gotchas-1.jpg 1215w, /static/a3c9e98e1e79f84baec5f4a9a122aa5e/04bec/service-worker-gotchas-1.jpg 1600w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/a3c9e98e1e79f84baec5f4a9a122aa5e/92b2a/service-worker-gotchas-1.jpg" alt="Service Worker lifecycle and events hierarchy" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>As you see, it all starts with install <code class="language-text">event</code> which is fired only once in worker's lifecycle. It is responsible for installing the service worker and initially caching the most important pages and assets of a website:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Then there's <code class="language-text">activate</code> event which is also fired only once in a lifecycle after the <code class="language-text">install</code> event and we will use it for deleting the old documents and files from cache:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'activate'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Finally - <code class="language-text">fetch</code>. It's so powerful that it lets you kinda intercept HTTP requests and return relatively anything you want. It is fired on each HTTP request and for each request separately, for example: say you have a tiny website which consists only of <code class="language-text">index.html</code>, <code class="language-text">app.css</code>, <code class="language-text">app.js</code>. files. When the user navigates to the website, the fetch event is triggered for each resource (three times in total) where you can choose what to return, be it a fresh page/asset or a copy from the cache.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="critical-and-non-critical-resources" style="position:relative;"><a href="#critical-and-non-critical-resources" aria-label="critical and non critical resources permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Critical and non-critical resources</h2> <p>You can choose to cache the initial resources along with the worker's installation. It's up to you picking a few most important ones, but usually they are a homepage, a pair of main CSS, JS files and of course "offline" page - this is enough for a basic <em>offline</em> experience.</p> <p>The <em>gotcha</em> here is that Service Worker is installed only when the initial resources are downloaded by a browser, consequently it is very important not to overload the process with too many files. Otherwise if the user spends a very small amount of time on the website and/or also has a slow internet connection the worker might not get installed at all.</p> <p>Now let's make a use of <code class="language-text">install</code> event:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> criticalResources <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token string">'/offline/'</span><span class="token punctuation">,</span> <span class="token string">'/assets/css/main.css'</span><span class="token punctuation">,</span> <span class="token string">'/assets/js/main.js'</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">cacheCriticals</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>version<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token parameter">cache</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>criticalResources<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span> <span class="token function">cacheCriticals</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Here we have <code class="language-text">criticalResources</code> array of the stuff we want to always be available for our worker (and thus users). Then there is a function <code class="language-text">cacheCriticals</code> which is executed in an asynchronous manner. The function kinda opens a cache room for the resources and puts them in there. The sign text on the door is a value of a custom variable <code class="language-text">version</code> which I elaborate on later in the article.</p> <p>Since it is recommended to have a very limited amount of critical resources, we can have an additional array of files that does not block an installation of our worker. Therefore we can enrich our strategy for <code class="language-text">install</code> event with "critical" and "important, but not critical"lists. Benefits are we get the worker installed as soon as possible along with the criticalscached whereas the resources from the new array will be cached asynchronously and only if/when possible. In order to achieve this, let's create an additional array and modify the function:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> otherResources <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'/about/'</span><span class="token punctuation">,</span> <span class="token string">'/contact/'</span><span class="token punctuation">,</span> <span class="token string">'/services/'</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">cacheCriticals</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>version<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token parameter">cache</span> <span class="token operator">=></span> <span class="token punctuation">{</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>otherResources<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// important, but not critical resources</span> <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>criticalResources<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// critical resources</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div> <p>The <code class="language-text">return</code> statement is important here as it labels a prerequisite for the worker to be installed.</p> <h2 id="service-worker-strategies" style="position:relative;"><a href="#service-worker-strategies" aria-label="service worker strategies permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker strategies</h2> <p>Let's move over to <code class="language-text">fetch</code> event which is a cornerstone. As I mentioned, there are two ways to operate when the request for a resource knocks on – it's online-first and offline-first. The <em>gotcha</em> here is that you can choose different <em>give-it-to-me</em> strategies for each type of resource. I'd exclude three common Service Worker strategies:</p> <h3 id="online-first-everything" style="position:relative;"><a href="#online-first-everything" aria-label="online first everything permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Online-first everything</h3> <p>This means serving resources from the network first and falling back to the cache storage for the failed network requests. This is probably the safest strategy as it eliminates the chance of serving an outdated content for Internet-connected users and provides a fallback for disconnected ones. However, the strategy provides no gains in page download performance for the online users.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> <span class="token comment">// try fetching a resource from the network</span> <span class="token function">fetch</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// cache the resource and serve it</span> <span class="token keyword">let</span> responseCopy <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">addToCache</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">,</span> responseCopy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this is a custom function, I'll elaborate on it later</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// the resource could not be fetched from network</span> <span class="token comment">// so let's pull it out from cache, otherwise serve the "offline" page</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> response <span class="token operator">||</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">'/offline/'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="offline-first-everything" style="position:relative;"><a href="#offline-first-everything" aria-label="offline first everything permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Offline-first everything</h3> <p>It's serving resources from cache first but falling back to network request if the resource hasn't been cached previously. The advantage here is that your website loads faster if cached: there will be no need for some network requests as things are pulled out right from the user's device.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> <span class="token comment">// check if the resource was cached before</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// serve the resource if cached, otherwise fetch it through the network</span> <span class="token keyword">return</span> response <span class="token operator">||</span> <span class="token function">fetch</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// cache the newly fetched resource and serve it</span> <span class="token keyword">let</span> responseCopy <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">addToCache</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">,</span> responseCopy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this is a custom function, I'll elaborate on it later</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// serve "offline" page if it couldn't be fetched from network</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">'/offline/'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Performance gains are visible to the naked eye, but every bean has its black: a chance exists your users may be served with an outdated content. When the user comes to your website for the first time our Service Worker is installed and the critical resources along with a version number are stashed into the browser's cache. After a while the user returns to your homepage which, let's say, displays a list of your latest blog posts. The worker then immediately checks if the requested HTML document had been cached previously and serves it from there if the answer is true. So in that case if you published a new blog post before the user returned to your website they would not be able to see the post in the list because the homepage was served from the cache. After rendering the homepage our worker then detects that there is a change in the resource's version number and deletes the HTML document from cache. Therefore in order to see the updated page the user should refresh it. This may not be a huge problem for representative websites, but vise-versa for frequently updates ones.</p> <h3 id="offline-first-assets-and-online-first-html-documents" style="position:relative;"><a href="#offline-first-assets-and-online-first-html-documents" aria-label="offline first assets and online first html documents permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Offline-first assets and online-first HTML documents</h3> <p>It's a combination of the first two strategies, but differentiated per resource types: offline-first assets and online-first HTML documents. The strategy partly solves the offline-first's <em>refresh</em> problem and so makes sure the users are always entertained with the latest content. The good thing is that you can check the request headers and so determine a type of the resource:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> type <span class="token operator">=</span> event<span class="token punctuation">.</span>request<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'Accept'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// network-first for HTML documents</span> <span class="token keyword">if</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span><span class="token string">'text/html'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> <span class="token comment">// respondWith code from network-first section</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// cache-first for assets</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> <span class="token comment">// respondWith code from cache-first section</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>This could be very suitable for CMS-based websites as you don't need to manage cache on every database update (content insertion). That's exactly what we adopted to our client's WordPress based site.</p> <h2 id="serving-offline-image" style="position:relative;"><a href="#serving-offline-image" aria-label="serving offline image permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Serving "offline" image</h2> <p>We have already learned how to cache and serve the "offline" page and turns out we can do the same with images: cache and serve a custom image for the image-type requests that failed. Another one nifty UX improvement, design is in details:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-2.jpeg?align=overflow" data-alt="Serving &quot;offline&quot; image" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.70935960591133%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2ZkMo//EABcQAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQEAAQUCTGykyH//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAARFRYf/aAAgBAQABPyFnBiZWkYNKkH//2gAMAwEAAgADAAAAEDAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARIUEQYaH/2gAIAQEAAT8QxRCaoY9BavRxqcgjkpOj2f/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/b0d0dc9ae0ae72bc58e5b1459cc34536/3466e/service-worker-gotchas-2.webp 203w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/d7d9a/service-worker-gotchas-2.webp 405w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/6da5f/service-worker-gotchas-2.webp 810w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/1eaf0/service-worker-gotchas-2.webp 1215w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/77fde/service-worker-gotchas-2.webp 1620w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/548e7/service-worker-gotchas-2.webp 2400w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/b0d0dc9ae0ae72bc58e5b1459cc34536/37414/service-worker-gotchas-2.jpg 203w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/32fb1/service-worker-gotchas-2.jpg 405w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/92b2a/service-worker-gotchas-2.jpg 810w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/290c6/service-worker-gotchas-2.jpg 1215w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/ddd99/service-worker-gotchas-2.jpg 1620w, /static/b0d0dc9ae0ae72bc58e5b1459cc34536/fdb5a/service-worker-gotchas-2.jpg 2400w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/b0d0dc9ae0ae72bc58e5b1459cc34536/92b2a/service-worker-gotchas-2.jpg" alt="Serving &quot;offline&quot; image" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Actually there are at least two ways of implementing the "offline" image:</p> <h3 id="inlined-in-workersfile" style="position:relative;"><a href="#inlined-in-workersfile" aria-label="inlined in workersfile permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Inlined in worker's file</h3> <p>If the image is technically simple you can Base64 it or ideally have it in SVG format which is a complete win: the image is resolution-independent, style-able, easily maintainable.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span>caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> response <span class="token comment">// cache-first</span> <span class="token operator">||</span> <span class="token function">fetch</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// network</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// offline</span> <span class="token keyword">if</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Response</span><span class="token punctuation">(</span><span class="token string">'&lt;svg...'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'image/svg+xml'</span><span class="token punctuation">,</span> <span class="token string-property property">'Cache-Control'</span><span class="token operator">:</span> <span class="token string">'no-store'</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="standalone-file" style="position:relative;"><a href="#standalone-file" aria-label="standalone file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Standalone file</h3> <p>If the image is more like a photo and Base64 or SVG conversion would not be rational, you ought to have it as a separate file placed on a server. Of course you should cache it as a critical resource when <code class="language-text">install</code> event occurs and then it can be served like a typical resource:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span>caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> response <span class="token comment">// cache-first</span> <span class="token operator">||</span> <span class="token function">fetch</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// network</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// offline</span> <span class="token keyword">if</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">'/offline.jpg'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="garbage-in-cache-is-yourproblem" style="position:relative;"><a href="#garbage-in-cache-is-yourproblem" aria-label="garbage in cache is yourproblem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Garbage in cache is your problem</h2> <p>It's a sign of a really bad UX if your users are disturbed with error messages about exceeded <a href="http://stackoverflow.com/questions/35242869/what-is-the-storage-limit-for-a-service-worker" target="_blank" rel="nofollow noopener noreferrer">memory quota</a>. Therefore it's us who have to take care of the contents of cache, because browsers have no mechanisms for deciding whether the particular resources should be deleted or not.</p> <h3 id="versioning" style="position:relative;"><a href="#versioning" aria-label="versioning permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Versioning</h3> <p>And so here the resource versioning kicks in. It begins by defining a version number or a key that will be used for caching new and deleting the old stuff. Instead of putting everything into a single place we will distribute different resource types across their own cache "rooms":</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> version <span class="token operator">=</span> <span class="token string">'1-'</span><span class="token punctuation">,</span> criticalsCacheName <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>version<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">critical</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> otherCacheName <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>version<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">other</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> imagesCacheName <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>version<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">images</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre></div> <p>Remember the <code class="language-text">addToCache</code> function we used under the <code class="language-text">fetch</code> event? Let's improve it by making it to accept an additional parameter – cache room name:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">addToCache</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">cacheName<span class="token punctuation">,</span> request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">cache</span> <span class="token operator">=></span> cache<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div> <p>The version number should be incremented along with every website update. Well, maybe not always "with every" - this depends on your Service Worker strategy. So with every increment pushed a new cache party is created in the browser. Resources are grouped by version numbers like this:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token string-property property">'1-critical'</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token string">'/about/'</span><span class="token punctuation">,</span> <span class="token string">'/offline/'</span><span class="token punctuation">,</span> <span class="token comment">// ...</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string-property property">'2-critical'</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token string">'/about/'</span><span class="token punctuation">,</span> <span class="token string">'/offline/'</span><span class="token punctuation">,</span> <span class="token comment">// ...</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">//...</span></code></pre></div> <p>In order to keep the cache lean, we should aways delete groups from previous lifecycles of the worker. Let's take an advantage of the <code class="language-text">activate</code> event and clean up the old stuff:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">clearOldCaches</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">keys</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>keys<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">key</span> <span class="token operator">=></span> <span class="token operator">!</span>key<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>version<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">key</span> <span class="token operator">=></span> caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'activate'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><span class="token function">clearOldCaches</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> self<span class="token punctuation">.</span>clients<span class="token punctuation">.</span><span class="token function">claim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>In our case, having the current version key equal to <code class="language-text">2-critical</code> will delete the <code class="language-text">1-critical</code> as the condition <code class="language-text">!key.startsWith(version)</code> is fulfilled: <code class="language-text">!'1-critical'.startsWith('2-')</code>.</p> <h3 id="additional-cachecontrol" style="position:relative;"><a href="#additional-cachecontrol" aria-label="additional cachecontrol permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Additional cache control</h3> <p>As I mentioned previously, this event is fired only once in a worker's lifecycle. The worker can live a long life therefore there is an increased chance the cache size may get exceed. A really good mechanism for wiping the dust was <a href="https://adactio.com/journal/9888" target="_blank" rel="nofollow noopener noreferrer">suggested by Jeremy Keith</a>. We can kinda create a custom event and send a trigger-like signal from the website to our Service Worker. An additional code next to worker's file registration:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/serviceworker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span>controller<span class="token punctuation">)</span> <span class="token punctuation">{</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span>controller<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">'command'</span><span class="token operator">:</span> <span class="token string">'trimCache'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>The <em>message</em> will be posted on every page visit - we can catch it and react accordingly, i.e. leave only a particular amount of resources by deleting the rest of them.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">trimCache</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">cacheName<span class="token punctuation">,</span> maxItems</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">cache</span> <span class="token operator">=></span> <span class="token punctuation">{</span> cache<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">keys</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>keys<span class="token punctuation">.</span>length <span class="token operator">></span> maxItems<span class="token punctuation">)</span> cache<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>keys<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token function">trimCache</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">,</span> maxItems<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'message'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>data<span class="token punctuation">.</span>command <span class="token operator">==</span> <span class="token string">'trimCache'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">trimCache</span><span class="token punctuation">(</span>otherCacheName<span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">trimCache</span><span class="token punctuation">(</span>imagesCacheName<span class="token punctuation">,</span> <span class="token number">25</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>We're not just keeping the cache size sane, but also ensuring we have the critical resources available since we do not apply trimming for <code class="language-text">criticalResources</code> room.</p> <p>Since we're separating our cache into several rooms, it's important to make sure the resources go where they are supposed to. Here's an improvement on how we cache things under <code class="language-text">fetch</code> event:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span>criticalResources<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>url<span class="token punctuation">.</span>pathname<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">addToCache</span><span class="token punctuation">(</span>criticalsCacheName<span class="token punctuation">,</span> request<span class="token punctuation">,</span> responseCopy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">addToCache</span><span class="token punctuation">(</span>imagesCacheName<span class="token punctuation">,</span> request<span class="token punctuation">,</span> responseCopy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token function">addToCache</span><span class="token punctuation">(</span>otherCacheName<span class="token punctuation">,</span> request<span class="token punctuation">,</span> responseCopy<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="omitting-thrid-party-resources" style="position:relative;"><a href="#omitting-thrid-party-resources" aria-label="omitting thrid party resources permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Omitting thrid-party resources</h3> <p>Websites usually contain various thrid-party resources like JS, CSS, image files, etc. Combine those kilobytes throughout the entire site and you may find yourself trying put an elephant into a canary's cage. Therefore omitting those resources by default and having a list of exceptions if needed is a good practice:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> thirdPartyExceptions <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'https://code.jquery.com/jquery.min.js'</span><span class="token punctuation">,</span> <span class="token comment">// ...</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><span class="token function">fetch</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// network</span> <span class="token comment">// cache only resources from this domain as well as the exceptions</span> <span class="token keyword">if</span><span class="token punctuation">(</span>location<span class="token punctuation">.</span>origin <span class="token operator">===</span> url<span class="token punctuation">.</span>origin <span class="token operator">||</span> thirdPartyExceptions<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>url<span class="token punctuation">.</span>href<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// addToCache use</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// ...</span></code></pre></div> <p><code class="language-text">location</code> is a global variable associated with the worker. With the code above we only allow resources from the server the website is hosted at as well as the exceptions.</p> <h3 id="forcing-serviceworkerjs-filerenewal" style="position:relative;"><a href="#forcing-serviceworkerjs-filerenewal" aria-label="forcing serviceworkerjs filerenewal permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Forcing serviceworker.js file renewal</h3> <p>Remember when I mentioned that the browser tries to redownload serviceworker.js file on every page request made by the user? Well, that's the theory. But in practice serviceworker.js file updates does not necessarily always have an instant reflection in the browser. Consequently, a chance exists that some of your users will see the outdated content even if there's fresh stuff available. In order to overcome this and have a new Service Worker lifecycle started as soon as the file is updated, I added a server-level configuration targeted to serviceworker.js file which tells the browser and the computer do not ever cache this file.</p> <h4 id="apache-way" style="position:relative;"><a href="#apache-way" aria-label="apache way permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Apache way</h4> <div class="gatsby-highlight" data-language="apacheconf"><pre class="language-apacheconf"><code class="language-apacheconf"><span class="token directive-block tag"><span class="token directive-block tag"><span class="token punctuation">&lt;</span>Files</span><span class="token directive-block-parameter attr-value"> serviceworker.js</span><span class="token punctuation">></span></span> <span class="token directive-inline property">FileETag</span> None <span class="token directive-inline property">Header</span> unset ETag <span class="token directive-inline property">Header</span> set Cache-Control <span class="token string">"max-age=0, no-cache, no-store, must-revalidate"</span> <span class="token directive-inline property">Header</span> set Pragma <span class="token string">"no-cache"</span> <span class="token directive-inline property">Header</span> set Expires <span class="token string">"Wed, 11 Jan 1984 05:00:00 GMT"</span> <span class="token directive-block tag"><span class="token directive-block tag"><span class="token punctuation">&lt;/</span>Files</span><span class="token punctuation">></span></span></code></pre></div> <h4 id="nginx-way" style="position:relative;"><a href="#nginx-way" aria-label="nginx way permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Nginx way</h4> <div class="gatsby-highlight" data-language="nginx"><pre class="language-nginx"><code class="language-nginx"><span class="token directive"><span class="token keyword">server</span></span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">location</span> ~* (serviceworker\.js)$</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">add_header</span> <span class="token string">'Cache-Control'</span> <span class="token string">'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'</span></span><span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">expires</span> <span class="token boolean">off</span></span><span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">proxy_no_cache</span> <span class="token number">1</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="service-worker-anddom" style="position:relative;"><a href="#service-worker-anddom" aria-label="service worker anddom permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker and DOM</h2> <p>Service Worker does not have a direct access to DOM at all, which means manipulating it is not possible. No <code class="language-text">window</code> nor <code class="language-text">document</code> variables are present, meanwhile globally available <code class="language-text">self</code> variable points to <code class="language-text">WorkerGlobalScope</code>, not <code class="language-text">Window</code> like we are used to.</p> <p>Anyway, just like we have proved before, we can still make a website and a Service Worker to communicate directly. <code class="language-text">postMessage</code> does this in <em>"to Service Worker"</em> direction and looks like it is also <a href="http://craig-russell.co.uk/2016/01/29/service-worker-messaging.html#.WRnuDFKB2Rs" target="_blank" rel="nofollow noopener noreferrer">possible</a> to do it in <em>"to website"</em> direction as well.</p> <h2 id="service-worker-for-multilingual-website" style="position:relative;"><a href="#service-worker-for-multilingual-website" aria-label="service worker for multilingual website permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker for multilingual website</h2> <p>Obviously, you may want to enrich the initial cache with more than just one language:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> criticalResources <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'/sv/'</span><span class="token punctuation">,</span> <span class="token string">'/en/'</span><span class="token punctuation">,</span> <span class="token string">'/sv/offline/'</span><span class="token punctuation">,</span> <span class="token string">'/en/offline/'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> otherResources <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'/sv/projekt/'</span><span class="token punctuation">,</span> <span class="token string">'/sv/om/'</span><span class="token punctuation">,</span> <span class="token string">'/sv/kollegor/'</span><span class="token punctuation">,</span> <span class="token string">'/sv/blogg/'</span><span class="token punctuation">,</span> <span class="token string">'/en/work/'</span><span class="token punctuation">,</span> <span class="token string">'/en/about/'</span><span class="token punctuation">,</span> <span class="token string">'/en/colleagues/'</span><span class="token punctuation">,</span> <span class="token string">'/en/blog/'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre></div> <p>But the <em>gotcha</em> lies behind the "offline" page: wouldn't it be nice to have an "offline" page enabled for each language?</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> appLangs <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'en'</span><span class="token punctuation">,</span> <span class="token string">'sv'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> url <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> <span class="token function">fetch</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> lang <span class="token operator">=</span> url<span class="token punctuation">.</span>pathname<span class="token punctuation">.</span><span class="token function">substr</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> lang <span class="token operator">=</span> appLangs<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>lang<span class="token punctuation">)</span> <span class="token operator">?</span> lang <span class="token operator">:</span> appLangs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lang<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/offline/</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Before serving the "offline" page the code above checks the first two characters of URL of the requested resource for a language code. It also verifies if this code is available in the languages list otherwise it picks the first entry from the list which is treated as a default.</p> <p>You may adapt this accordingly to your multilingual URL strategy. My example relies on what we have here at Kollegorna: all of the page addresses start with language code.</p> <h2 id="service-worker-is-backend-dependent" style="position:relative;"><a href="#service-worker-is-backend-dependent" aria-label="service worker is backend dependent permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Service Worker is backend-dependent</h2> <p>In one of the previous sections of the article we realized the importance of versioning. The method of updating a value for <code class="language-text">version</code> variable depends on your backend system and Service Worker strategy. If you are not looking to automate everything or got a simple static website that does not rely on a CMS, you may just edit Service Worker file manually and increment the variable's value or enter something random when updating the site.</p> <p>The real fun begins if you prefer to have everything fully automated. For example, you've got a Wordpress based website and gone the offline-first approach way. In order to get the version number incremented on content updates you can write a custom hook function called when inserting or updating a post:</p> <div class="gatsby-highlight" data-language="php"><pre class="language-php"><code class="language-php"><span class="token keyword">function</span> <span class="token function-definition function">update_sw_version</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// writes serviceworker.js file with a new value for "version" variable</span> <span class="token punctuation">}</span> <span class="token function">add_action</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'save_post'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'update_sw_version'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>We have enabled automated Service Workers on Trellis (Wordpress LEMP stack) and Middleman (static site generator) based sites, but I will elaborate on this in my future articles.</p> <h2 id="debugging-serviceworker" style="position:relative;"><a href="#debugging-serviceworker" aria-label="debugging serviceworker permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Debugging Service Worker</h2> <p>I found Chrome DevTools to be a very handy tool for debugging Service Workers. It has this nice Network Throttling feature which makes it easy to simulate offline experience:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-3.jpeg" data-alt="Debugging Service&amp;nbsp;Worker"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 63.05418719211823%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd2JYQw//8QAGBAAAgMAAAAAAAAAAAAAAAAAABABESH/2gAIAQEAAQUCNUW//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAEDL/2gAIAQEABj8Caf/EABgQAAMBAQAAAAAAAAAAAAAAAAABETEh/9oACAEBAAE/IYa4LBGcoxEih//aAAwDAQACAAMAAAAQQw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAhMUFRYf/aAAgBAQABPxCHoyl1q6vGBNpXDsP3ODue4AAQCG8//9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/f0b9927652b033fe11d6fee0f0438c72/3466e/service-worker-gotchas-3.webp 203w, /static/f0b9927652b033fe11d6fee0f0438c72/d7d9a/service-worker-gotchas-3.webp 405w, /static/f0b9927652b033fe11d6fee0f0438c72/6da5f/service-worker-gotchas-3.webp 810w, /static/f0b9927652b033fe11d6fee0f0438c72/1eaf0/service-worker-gotchas-3.webp 1215w, /static/f0b9927652b033fe11d6fee0f0438c72/08048/service-worker-gotchas-3.webp 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/f0b9927652b033fe11d6fee0f0438c72/37414/service-worker-gotchas-3.jpg 203w, /static/f0b9927652b033fe11d6fee0f0438c72/32fb1/service-worker-gotchas-3.jpg 405w, /static/f0b9927652b033fe11d6fee0f0438c72/92b2a/service-worker-gotchas-3.jpg 810w, /static/f0b9927652b033fe11d6fee0f0438c72/290c6/service-worker-gotchas-3.jpg 1215w, /static/f0b9927652b033fe11d6fee0f0438c72/5814a/service-worker-gotchas-3.jpg 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/f0b9927652b033fe11d6fee0f0438c72/92b2a/service-worker-gotchas-3.jpg" alt="Debugging Service&amp;nbsp;Worker" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Debugging worker might be tricky sometimes, because you first have to unregister the current worker before testing the updated code, otherwise you'll find yourself doing a Sisyphus work - investigating the old code. In Chrome you can unregister the worker manually via <code class="language-text">DevTools → Application → Service Workers</code>:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-4.jpeg" data-alt="Debugging Service&amp;nbsp;Worker"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 35.960591133004925%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2qCgf//EABUQAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAgv/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAADAQAAAAAAAAAAAAAAAAAAEWEB/9oACAEBAAE/IbaRCP/aAAwDAQACAAMAAAAQ+8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAwEBAAAAAAAAAAAAAAAAARExgZHR/9oACAEBAAE/EHO3WoceCuWzHD//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/8e0048703acfdc1b0a323666e82b7c3a/3466e/service-worker-gotchas-4.webp 203w, /static/8e0048703acfdc1b0a323666e82b7c3a/d7d9a/service-worker-gotchas-4.webp 405w, /static/8e0048703acfdc1b0a323666e82b7c3a/6da5f/service-worker-gotchas-4.webp 810w, /static/8e0048703acfdc1b0a323666e82b7c3a/1eaf0/service-worker-gotchas-4.webp 1215w, /static/8e0048703acfdc1b0a323666e82b7c3a/f0497/service-worker-gotchas-4.webp 1516w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/8e0048703acfdc1b0a323666e82b7c3a/37414/service-worker-gotchas-4.jpg 203w, /static/8e0048703acfdc1b0a323666e82b7c3a/32fb1/service-worker-gotchas-4.jpg 405w, /static/8e0048703acfdc1b0a323666e82b7c3a/92b2a/service-worker-gotchas-4.jpg 810w, /static/8e0048703acfdc1b0a323666e82b7c3a/290c6/service-worker-gotchas-4.jpg 1215w, /static/8e0048703acfdc1b0a323666e82b7c3a/fb728/service-worker-gotchas-4.jpg 1516w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/8e0048703acfdc1b0a323666e82b7c3a/92b2a/service-worker-gotchas-4.jpg" alt="Debugging Service&amp;nbsp;Worker" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>In some cases I find <code class="language-text">Clear Storage</code> to work better for me as it completely wipes out all the cached stuff and I am always served with the latest one when debugging:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-5.jpeg" data-alt="Debugging Service&amp;nbsp;Worker"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 67.48768472906403%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHZtVwJD//EABYQAQEBAAAAAAAAAAAAAAAAAAEQIP/aAAgBAQABBQIwT//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAIBBQAAAAAAAAAAAAAAAAABERAhMUGh/9oACAEBAAE/IUe3wSsQRCMKf//aAAwDAQACAAMAAAAQww//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEQQVGR/9oACAEBAAE/EKqqhpELT0lOEA1cisrsx//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/b81529c8272c8e8c7c27f004b2767a63/3466e/service-worker-gotchas-5.webp 203w, /static/b81529c8272c8e8c7c27f004b2767a63/d7d9a/service-worker-gotchas-5.webp 405w, /static/b81529c8272c8e8c7c27f004b2767a63/6da5f/service-worker-gotchas-5.webp 810w, /static/b81529c8272c8e8c7c27f004b2767a63/1eaf0/service-worker-gotchas-5.webp 1215w, /static/b81529c8272c8e8c7c27f004b2767a63/08048/service-worker-gotchas-5.webp 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/b81529c8272c8e8c7c27f004b2767a63/37414/service-worker-gotchas-5.jpg 203w, /static/b81529c8272c8e8c7c27f004b2767a63/32fb1/service-worker-gotchas-5.jpg 405w, /static/b81529c8272c8e8c7c27f004b2767a63/92b2a/service-worker-gotchas-5.jpg 810w, /static/b81529c8272c8e8c7c27f004b2767a63/290c6/service-worker-gotchas-5.jpg 1215w, /static/b81529c8272c8e8c7c27f004b2767a63/5814a/service-worker-gotchas-5.jpg 1400w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/b81529c8272c8e8c7c27f004b2767a63/92b2a/service-worker-gotchas-5.jpg" alt="Debugging Service&amp;nbsp;Worker" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Since Service Worker is a relatively new technology it could not do without alteration on interpreting various rules and directives. Therefore always be sure to test your worker on the future browser releases like <a href="https://www.google.com/chrome/browser/canary.html" target="_blank" rel="nofollow noopener noreferrer">Chrome Canary</a> or <a href="https://www.mozilla.org/en-US/firefox/developer" target="_blank" rel="nofollow noopener noreferrer">Firefox Developer Edition</a>.</p> <p>Once you have your worker up and running, you can reasonably hope for 100/100 score on Chrome's website performance evaluation tool - <a href="https://www.google.com/url?sa=t&#x26;rct=j&#x26;q=&#x26;esrc=s&#x26;source=web&#x26;cd=1&#x26;cad=rja&#x26;uact=8&#x26;ved=0ahUKEwit69ai4orUAhXC2CwKHXVXBMgQFggvMAA&#x26;url=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdetail%2Flighthouse%2Fblipmdconlkpinefehnmjammfjpmpbjk%3Fhl%3Den&#x26;usg=AFQjCNFvomjeSTNsyil51bzJfvzQWOp_lA&#x26;sig2=Zo4u5jzNkdhXR0pkoXNiHg" target="_blank" rel="nofollow noopener noreferrer">Lighthouse</a>:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/service-worker-gotchas-6.jpeg" data-alt="Debugging Service&amp;nbsp;Worker"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.21674876847291%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAEDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAezpRVH/xAAYEAEAAwEAAAAAAAAAAAAAAAABAAIQA//aAAgBAQABBQI51pDGGf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAERICL/2gAIAQEABj8CyFX/xAAZEAADAAMAAAAAAAAAAAAAAAABECEAMXH/2gAIAQEAAT8hFR4ygtG//9oADAMBAAIAAwAAABDLD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxBV1n//xAAcEAABBAMBAAAAAAAAAAAAAAABABAhQRExcYH/2gAIAQEAAT8QKyZuUozZ6scb1Lf/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/9db84b0ccd5b3c2e7090f52b7787838b/3466e/service-worker-gotchas-6.webp 203w, /static/9db84b0ccd5b3c2e7090f52b7787838b/d7d9a/service-worker-gotchas-6.webp 405w, /static/9db84b0ccd5b3c2e7090f52b7787838b/6da5f/service-worker-gotchas-6.webp 810w, /static/9db84b0ccd5b3c2e7090f52b7787838b/1eaf0/service-worker-gotchas-6.webp 1215w, /static/9db84b0ccd5b3c2e7090f52b7787838b/64296/service-worker-gotchas-6.webp 1600w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/9db84b0ccd5b3c2e7090f52b7787838b/37414/service-worker-gotchas-6.jpg 203w, /static/9db84b0ccd5b3c2e7090f52b7787838b/32fb1/service-worker-gotchas-6.jpg 405w, /static/9db84b0ccd5b3c2e7090f52b7787838b/92b2a/service-worker-gotchas-6.jpg 810w, /static/9db84b0ccd5b3c2e7090f52b7787838b/290c6/service-worker-gotchas-6.jpg 1215w, /static/9db84b0ccd5b3c2e7090f52b7787838b/04bec/service-worker-gotchas-6.jpg 1600w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/9db84b0ccd5b3c2e7090f52b7787838b/92b2a/service-worker-gotchas-6.jpg" alt="Debugging Service&amp;nbsp;Worker" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <hr> <p>Let me end up the article with an advice: don't ever take Service Worker code advices for granted. Always test them, because adaptations are very individual and what worked for one doesn't automatically mean it will work for you.</p><![CDATA[Lazy-Loading Disqus Comments]]>https://osvaldas.info/lazy-loading-disqus-commentshttps://osvaldas.info/lazy-loading-disqus-commentsWed, 07 Dec 2016 00:00:00 GMT<p>Lately, I've been obsessed with optimizing performance through lazy-loading. Recently, I've written on how to <a href="/lazy-loading-google-maps">lazy-load Google Maps</a> and on how to <a href="https://css-tricks.com/lazy-loading-responsive-adsense-ads" target="_blank" rel="nofollow noopener noreferrer">lazy-load responsive Google Adsense</a>. Now it's time for <a href="https://disqus.com" target="_blank" rel="nofollow noopener noreferrer">Disqus</a>, a service for embedding comments on your website. It's a great service. It eliminates the headache of developing your own local commenting system, dealing with spam, etc. Recently, I've been working on implementing the widget in one of my projects.</p> <p>I've written an article on that and got it published on <a href="https://css-tricks.com/lazy-loading-disqus-comments" target="_blank" rel="nofollow noopener noreferrer">CSS-Tricks</a>.</p> <p><img src="/ddc86aaaf734caada599682b5a3ccb9f/disqus-comments-lazyloaded.gif" alt="Disqus Comments: Lazyloaded"></p> <ul> <li><a href="https://css-tricks.com/lazy-loading-disqus-comments" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a></li> <li><a href="https://v3.osvaldas.info/examples/lazy-loading-disqus-comments" target="_blank" rel="nofollow noopener noreferrer"><strong>Try the demo</strong></a></li> </ul> <p>You can also contribute, follow the project on <a href="https://github.com/osvaldasvalutis/disqusLoader.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a>.</p><![CDATA[Enabling CodeIgniter's Garbage Collector]]>https://osvaldas.info/enabling-codeigniters-garbage-collectorhttps://osvaldas.info/enabling-codeigniters-garbage-collectorTue, 22 Nov 2016 00:00:00 GMT<p>My session driver of choice for CodeIgniter is database. A while ago, I noticed millions of rows in the corresponding database table. That means that garbage collector was not working. I checked <code class="language-text">config.php</code> for <code class="language-text">sess_regenerate_destroy</code>, but it was already set to <code class="language-text">false</code>. It was really weird because the problem seemed to have come out of nowhere – not much ago things were working just fine.</p> <p>After a short investigation, I found out this was and still is a common problem for those who had upgraded their CodeIgniter version from 2.x to 3.x. Turns out, the later possesses heavy changes for Sessions library. And most importantly – fundamental changes in dealing with garbage: CodeIgniter 3.x relies on PHP's native grabage collector. So, in order to get things working again, we need to configure PHP properly.</p> <h2 id="the-solution" style="position:relative;"><a href="#the-solution" aria-label="the solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The Solution</h2> <p>Today there are three PHP-native settings for dealing with session garbage collector. CodeIgniter's Session library has already taken care of one of them – <code class="language-text">gc_maxlifetime</code> – so there's no need for an extra touch here. The problem lies within the rest: <code class="language-text">session.gc_probability</code> and/or <code class="language-text">session.gc_divisor</code>. These two determine when the garbage collector is running. Here's what <a href="http://php.net/manual/en/session.configuration.php#ini.session.gc-probability" target="_blank" rel="nofollow noopener noreferrer">php.net says</a>:</p> <blockquote> <p>session.gc_divisor coupled with session.gc_probability defines the probability that the gc (garbage collection) process is started on every session initialization. The probability is calculated by using gc_probability/gc_divisor, e.g. 1/100 means there is a 1% chance that the GC process starts on each request.</p> </blockquote> <p>In my case, using <code class="language-text">ini_get()</code> revealed the following values: <code class="language-text">0</code> and <code class="language-text">1000</code>. The equation <code class="language-text">0/1000=0</code> meant there was no chance the garbage collector would even run.</p> <p>Finally, I solved the problem by using the values officially stated as default and by putting the following lines of PHP code into <code class="language-text">config/config.php</code> file:</p> <div class="gatsby-highlight" data-language="php"><pre class="language-php"><code class="language-php"><span class="token function">ini_set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'session.gc_probability'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">ini_set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'session.gc_divisor'</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Theoretically, this means that the garbage collector usually runs once every 100th request. It's more than enough, but it is up to you whether that causes too many unnecessary hits to database. An alternative solution for heavy-traffic websites could be having a 0% probability on the production site and a Cronjob-based script which removes database rows of the expired sessions.</p><![CDATA[Lazy Loading Responsive Adsense Ads]]>https://osvaldas.info/lazy-loading-responsive-adsense-adshttps://osvaldas.info/lazy-loading-responsive-adsense-adsTue, 15 Nov 2016 00:00:00 GMT<p>You've been hard at work optimizing your site. You've already done things like <a href="/lazy-loading-google-maps">lazy-loading Google Maps</a> and been wondering if there was anything else you could do. For example, is there anything we can do to improve the loading of ads? Good news, there is some things you can do. You can respect user's mobile data plan by loading ads only when they are likely to appear in the viewport zone. You can also serve ads in the right size in accordance to the screen of the device. That would be nice of you. That would not only be responsive but also responsible.</p> <p>I've written an article on that and got it published on <a href="http://css-tricks.com/lazy-loading-responsive-adsense-ads" target="_blank" rel="nofollow noopener noreferrer">CSS-Tricks</a>.</p> <p><img src="/1468151d04a907cc70c9ed922817279d/google-adsense-no-lazy-load.gif" alt="Google Adsense: No Lazy-Load"></p> <ul> <li><a href="https://css-tricks.com/lazy-loading-responsive-adsense-ads" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a></li> <li><a href="https://v3.osvaldas.info/examples/lazy-loading-responsive-adsense-ads" target="_blank" rel="nofollow noopener noreferrer"><strong>Try the demo</strong></a></li> </ul> <p>You can also examine the demo on <a href="http://codepen.io/osvaldas/pen/XNmNQN" target="_blank" rel="nofollow noopener noreferrer">CodePen</a> and contribute, follow the project on <a href="https://github.com/osvaldasvalutis/adsenseLoader.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a>.</p><![CDATA[How to Display Publish Dates as Time Since Posted]]>https://osvaldas.info/how-to-display-publish-dates-as-time-since-postedhttps://osvaldas.info/how-to-display-publish-dates-as-time-since-postedThu, 11 Feb 2016 00:00:00 GMT<p><img src="/17453ea53e57c4f32bbdd28e986d5b8a/how-to-display-publish-dates-as-time-since-posted.gif" alt="How to Display Publish Dates as Time Since Posted"></p> <p>It’s common to present dates on the Web in a format such as <em>"Published on September 12th, 2015"</em>, or <em>"09/12/2015 09:41:23"</em>.</p> <p>Each of these examples tells the full date and/or time of some kind of activity – be it a published article, or a reader comment, or perhaps an uploaded video.</p> <p>Date formats like this might seem perfectly reasonable. After all, they’re informative and human-readable. Well yes, but “human-readable” doesn’t necessary mean users will readily be able to understand how recently the activity has occurred. The Web is a fast-moving place, and giving your content a sense of freshness could be the key to engaging with your audience.</p> <p>I combined my ideas and practical solutions into an article which you are very welcome to read on <a href="http://www.sitepoint.com/counting-the-ago-time-how-to-keep-publish-dates-fresh/" target="_blank" rel="nofollow noopener noreferrer">SitePoint</a>.</p> <ul> <li><a href="http://www.sitepoint.com/counting-the-ago-time-how-to-keep-publish-dates-fresh/" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a></li> <li><a href="http://osvaldas.info/examples/counting-ago-time/" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a></li> </ul><![CDATA[Drag and Drop File Uploading]]>https://osvaldas.info/drag-drop-file-uploadinghttps://osvaldas.info/drag-drop-file-uploadingTue, 01 Dec 2015 00:00:00 GMT<p><img src="/ec157bcede3dd06b5fb454b7827912c0/drag-drop-upload-1.gif" alt="Drag and Drop File Uploading"></p> <p>When working on <del>Readerrr</del>, I wanted to enrich the experience of uploading an OPML file for importing feeds from other readers. There is nothing wrong with the traditional way for uploading a file, but it is a good thing to provide the alternative way – drag &#x26; drop file uploading. I've combined my experience into an article which you are very welcome to read on <a href="https://css-tricks.com/drag-and-drop-file-uploading" target="_blank" rel="nofollow noopener noreferrer">CSS-Tricks</a>.</p> <ul> <li><a href="https://css-tricks.com/drag-and-drop-file-uploading/" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a></li> <li><a href="https://css-tricks.com/examples/DragAndDropFileUploading/" target="_blank" rel="nofollow noopener noreferrer"><strong>Try the demo</strong></a></li> </ul><![CDATA[Customizing File Inputs the Smart Way]]>https://osvaldas.info/smart-custom-file-inputhttps://osvaldas.info/smart-custom-file-inputThu, 08 Oct 2015 00:00:00 GMT<p><img src="/08f68841253d309f1d9c12638d96b29f/smart-custom-file-input-5.gif" alt="Custome file input in action"></p> <p>And so it happened that I published the latest post of mine on <a href="http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/" target="_blank" rel="nofollow noopener noreferrer">Codrops</a>, not here this time. It’s about styling <code class="language-text">&lt;input type="file" /></code> by taking the advantage of <code class="language-text">&lt;label></code>. The technique is not perfect – nothing is perfect that imitates native HTML elements – but having in mind its pros &#x26; cons, I found it to be the most appropriate solution for styling file inputs.</p> <ul> <li><a href="http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way" target="_blank" rel="nofollow noopener noreferrer"><strong>Read the article</strong></a>.</li> <li><a href="http://tympanus.net/Tutorials/CustomFileInputs" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</li> </ul><![CDATA[Real-Time Search in JavaScript]]>https://osvaldas.info/real-time-search-in-javascripthttps://osvaldas.info/real-time-search-in-javascriptTue, 11 Aug 2015 00:00:00 GMT<p>What I meant was scanning the DOM of a page for text equivalents and showing the actual parts of the page, as well as hiding the irrelevant ones. I came up with the technique when I was designing <del>Readerrr’s</del> FAQ page. Take a look at the example:</p> <p><img src="/e0697f0f0de6f4f66ff3415641fd65b6/real-time-search-in-javascript.gif" alt="Real-Time Search with JavaScript"></p> <p><del>I have also implemented the solution here on my blog.</del></p> <h2 id="how-it-works" style="position:relative;"><a href="#how-it-works" aria-label="how it works permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>How it works</h2> <p>All simple. Let’s take the FAQ page as an example. Here’s a typical markup:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>FAQ<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>faq<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Type some keywords (e.g. giza, babylon, colossus)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>faq-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#faq-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Great Pyramid of Giza<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>The Great Pyramid of Giza <span class="token comment">&lt;!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>faq-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#faq-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Hanging Gardens of Babylon<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>The Hanging Gardens of Babylon <span class="token comment">&lt;!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>faq__notfound<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>No matches were found.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>I wrote a tiny piece of JavaScript code to handle the interaction and this is how it works:</p> <ol> <li>When the page loads, the script indexes the content of all <code class="language-text">li</code>’s into browser’s memory.</li> <li>When a user types text into the search field, the script searches for equivalents among the indexed data and hides the corresponding <code class="language-text">li</code>’s where no equivalents were found. If nothing found, a message is shown.</li> <li>The script highlights the text equivalents by replacing phases, for example, <code class="language-text">babylon</code> becomes <code class="language-text">&lt;span class="highlight">babylon&lt;/span></code>.</li> </ol> <p>Now, try it yourself:</p> <p><a href="https://v3.osvaldas.info/examples/real-time-search-in-javascript" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <h2 id="taking-it-further" style="position:relative;"><a href="#taking-it-further" aria-label="taking it further permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Taking it further</h2> <p>Since I chose FAQ page as an example, there are some issues to deal with.</p> <h3 id="toggling-the-answers" style="position:relative;"><a href="#toggling-the-answers" aria-label="toggling the answers permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Toggling the answers</h3> <p>It is a good practice to hide the answers by default and show them only when user needs them, that is to say when they <em>press</em> the question:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.faq > ul > li:not(.is-active) > div</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>document<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token string">'.faq h2 a'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">parents</span><span class="token punctuation">(</span><span class="token string">'li'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toggleClass</span><span class="token punctuation">(</span><span class="token string">'is-active'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>In the CSS part I use <em>child combinator selector</em> <code class="language-text">></code> because I don’t want to select and, therefore, to hide the elements of an answer, which may contain lists and <em>div’s</em>.</p> <h3 id="what-if-javascript-is-disabled" style="position:relative;"><a href="#what-if-javascript-is-disabled" aria-label="what if javascript is disabled permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What if JavaScript is disabled</h3> <p>The user won’t be able to see the answers. Unless you show them by default or develop a JavaScript-less solution. To do this, take a closer look at these fragments of the markup:</p> <ul> <li><code class="language-text">&lt;li id="faq-1"></code></li> <li><code class="language-text">&lt;a href="#faq-1"></code></li> </ul> <p>The usage of <em>fragment identifiers</em> enables us to take the advantage of CSS’s pseudo selector <code class="language-text">:target</code>:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.faq > ul > li:not(:target) > div</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Furthermore, the real-time search is not possible as well. But you can either provide a sever-side search possibility or hide the search field and so as not to confuse the user:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- remove this if you use Modernizr --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e<span class="token punctuation">,</span>t<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> r<span class="token operator">=</span>e<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"html"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>r<span class="token punctuation">.</span>className<span class="token operator">=</span>r<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">(^|\s)no-js(\s|$)</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><span class="token string">"$1$2"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span>window<span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div> <p>I added a class name <code class="language-text">no-js</code> to <code class="language-text">&lt;html></code> element. The <code class="language-text">&lt;script></code> part removes that class name. If JavaScript support is disabled in a browser, the class name won’t be removed; therefore:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.no-js .faq input</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>The <em>no-js</em> is a very handy technique, you can use it site-wide.</p> <h3 id="improving-ux" style="position:relative;"><a href="#improving-ux" aria-label="improving ux permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Improving UX</h3> <p>If there is only one list item that matches user’s query, it is a good practice to automatically show the content of that item, without requiring to press the title. To see what I mean, head over the <a href="#image-1">GIF</a> at the beginning of the post.</p> <h3 id="hidden-keywords" style="position:relative;"><a href="#hidden-keywords" aria-label="hidden keywords permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Hidden keywords</h3> <p>Here on my blog I have a <em>filterable</em> list of blog post titles only. Each post has some related keywords assigned. So, during the search, how do I make an item discoverable even if the title does not consist of a particular keyword? For example, how can I make the entry <em>“Real-Time Search in JavaScript”</em> visible if a user entered <em>“jquery”</em>? Yes, exactly, that is adding keywords and hiding them with CSS:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/real-time-search-in-javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Real-Time Search in JavaScript<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hidden-keywords<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>jquery filter input html css<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.hidden-keywords</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>A simple trick but not always that obvious.</p> <hr> <p>You will find two versions of the code in the source of the demo: without dependencies and jQuery-dependent. These versions are also divided into three groups of code so you can adapt only what your project needs.</p> <p><a href="https://v3.osvaldas.info/examples/real-time-search-in-javascript" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p><![CDATA[Detecting CSS Animation and Transition End with JavaScript]]>https://osvaldas.info/detecting-css-animation-transition-end-with-javascripthttps://osvaldas.info/detecting-css-animation-transition-end-with-javascriptTue, 23 Jun 2015 00:00:00 GMT<p>Detecting the end of CSS animation and transition could be useful if you want to back up some of your JavaScript behavior on CSS. The most common uses of this in my practice are:</p> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/131424355" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>Animating the <code class="language-text">display: none</code>. I used the technique on <del>Readerrr</del> for tabbed content and modal boxes: the new content or modal box shows up only after the old one disappears. And…</p> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/131424353" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>Animating the disappearance of an element and then removing it from the DOM. I did this here on the cart of my <a href="/shop">shop</a>.</p> <h2 id="problem" style="position:relative;"><a href="#problem" aria-label="problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Problem</h2> <p>Let’s take the shopping cart case as an example. Usually, I remove an item from the cart in this way:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.item'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addClass</span><span class="token punctuation">(</span><span class="token string">'disappear'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'webkitAnimationEnd mozAnimationEnd oAnimationEnd oanimationend animationend'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The problem with the code above is that it does not <em>degrade gracefully</em>: the callback would not be fired if the browser (i.e. IE 9-) did not support CSS animations. Also, the callback wouldn’t be fired if for some reason the duration of an animation was set to <code class="language-text">0s</code>. To sum it up, using this code is not safe enough.</p> <h2 id="solution" style="position:relative;"><a href="#solution" aria-label="solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Solution</h2> <p>In order to avoid these flaws, you can use these tiny helper functions I wrote:</p> <ul> <li><code class="language-text">onCSSAnimationEnd</code>​ – for <strong>animations</strong>;</li> <li><code class="language-text">CSSTransitionEnd​</code> – for <strong>transitions</strong>;</li> </ul> <p>Using them is that simple:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.item'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addClass</span><span class="token punctuation">(</span><span class="token string">'disappear'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">onCSSAnimationEnd</span><span class="token punctuation">(</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>There is a version of <strong>no-dependencies</strong> JavaScript code for your service:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> item <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.item'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> item<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'disappear'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> item<span class="token punctuation">.</span><span class="token function">onCSSAnimationEnd</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> item<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The source of both functions are available for download:</p> <ul> <li><strong><a href="https://v3.osvaldas.info/examples/detecting-css-animation-transition-end-with-javascript/oncssanimationend.js" target="_blank" rel="nofollow noopener noreferrer">oncssanimationend.js</a></strong>; no dependencies (IE 9+);<br> <em>360 bytes</em> bytes when minified and gzipped;</li> <li><strong><a href="https://v3.osvaldas.info/examples/detecting-css-animation-transition-end-with-javascript/jquery.oncssanimationend.js" target="_blank" rel="nofollow noopener noreferrer">jquery.oncssanimationend.js</a></strong>; jQuery dependency (compatible with v1 and v2);<br> <em>322 bytes</em> when minified and gzipped;</li> </ul> <p>Finally, check out the demo page to see the functions in action and inspect the source code for implementation on your next project:</p> <p><a href="https://v3.osvaldas.info/examples/detecting-css-animation-transition-end-with-javascript/" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a></p><![CDATA[Placeholder Polyfill with Password Support]]>https://osvaldas.info/placeholder-polyfill-with-password-supporthttps://osvaldas.info/placeholder-polyfill-with-password-supportFri, 17 Apr 2015 00:00:00 GMT<p>I use input placeholders in two ways: <em>(1)</em> as labels in simple forms that have just a few input fields and <em>(2)</em> as tips or suggestions in complex forms:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Your email address<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre></div> <p><img src="/80d6b4d56ed5af3ba9ee132fdf93778c/placeholder-polyfill-with-password-support-1.gif" alt="Placeholder Polyfill with Password Support"></p> <p>The problem with placeholders is that the feature is <strong><a href="http://caniuse.com/#feat=css-placeholder" target="_blank" rel="nofollow noopener noreferrer">not supported</a> in IE 9</strong> and below. This is a huge pain if you use placeholders as labels…</p> <h2 id="javascript-polyfill" style="position:relative;"><a href="#javascript-polyfill" aria-label="javascript polyfill permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript polyfill</h2> <p>​I have written a tiny jQuery-dependent plugin which enables placeholders in browsers that do not support the technology:</p> <ol> <li><a href="https://v3.osvaldas.info/examples/placeholder-polyfill-with-password-support/jquery.input-placeholder-polyfill.js" target="_blank" rel="nofollow noopener noreferrer"><strong>jquery.input-placeholder-polyfill.js</strong></a> (uncompressed; 2 KB);</li> <li><a href="https://v3.osvaldas.info/examples/placeholder-polyfill-with-password-support/jquery.input-placeholder-polyfill.min.js" target="_blank" rel="nofollow noopener noreferrer"><strong>jquery.input-placeholder-polyfill.min.js</strong></a> (minified; 966 bytes).</li> </ol> <p>Basic usage example:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.input-placeholder-polyfill.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'input'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inputPlaceholderPolyfill</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p>Or you can target input fields by a specific selector:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.textfield'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inputPlaceholderPolyfill</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p><strong>The plugin takes care of <em>textareas</em> and text-oriented inputs, including password type.</strong> Some other polyfills I have tried before had this bug, when you entered the text which was equal to input‘s placeholder value the input automatically cleared out, became blank. This is fixed in my version.</p> <p>How the plugin works? Firstly, it detects if the browser does not support input placeholders. Then if the <code class="language-text">[placeholder]</code> of <code class="language-text">&lt;input /></code> has a non-empty value, the plugin sets the same value for <code class="language-text">[value]</code> attribute and adds a class name for the input (<code class="language-text">is-placeholder</code> by default). The plugin also makes various checks and actions on input <code class="language-text">focus</code>, <code class="language-text">blur</code> and form <code class="language-text">submit</code> events to keep the values of <code class="language-text">[placeholder]</code> and <code class="language-text">[value]</code> correct.</p> <p>What is the <code class="language-text">is-placeholder</code> class name for? Firstly, it‘s for styling the input when it has a placeholder mode enabled. Just one additional line next to the styles for native placeholder:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.textfield.is-placeholder</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> grey <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.textfield::-webkit-input-placeholder</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> grey <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.textfield::-moz-placeholder</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> grey <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.textfield:-ms-input-placeholder</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> grey <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Secondly, it‘s for managing the input via JavaScript. The classic example is validating input values. Since the plugin uses <code class="language-text">[value]</code> for placeholder text, you have to make sure you are validating the actual value (the one that user enters), not the placeholder text:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'submit'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'.textfield'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $<span class="token keyword">this</span> <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">,</span> val <span class="token operator">=</span> $<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>$<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'is-placeholder'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> val <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>If you need, you can have a custom class name:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.textfield'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inputPlaceholderPolyfill</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">'textfield--placeholder'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>But… what about browsers that do not support placeholders and has JavaScript disabled?</p> <h2 id="css-fallback" style="position:relative;"><a href="#css-fallback" aria-label="css fallback permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS fallback</h2> <p>You can use a combination of both: JavaScript polyfill and CSS fallback for it, or you can bypass the JavaScript part and completely rely on CSS fallback. Or vice versa. Your situation dictates what you should do. In the end, the user should be able to use your website.</p> <h3 id="css-fallback-as-a-complement-to-javascript-polyfill" style="position:relative;"><a href="#css-fallback-as-a-complement-to-javascript-polyfill" aria-label="css fallback as a complement to javascript polyfill permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS fallback as a complement to JavaScript polyfill</h3> <p>Unlike in JavaScript part, it is impossible to do placeholders feature detection in CSS. The only thing left is targeting specific browsers – IE 9 and below in this case.</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token comment">&lt;!--[if lte IE 9]> &lt;html lang="en" class="no-js no-placeholder"> &lt;![endif]--></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>!--[if</span> <span class="token attr-name">gt</span> <span class="token attr-name">IE</span> <span class="token attr-name">9]</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>!--</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment">&lt;!--&lt;![endif]--></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token comment">// removes "no-js" class name from &lt;html> element; remove this &lt;script> part if you use Modernizr, because it will remove the class name for you</span> <span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window<span class="token punctuation">,</span> document</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">=</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">'no-js'</span><span class="token punctuation">,</span> <span class="token string">'js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span></code></pre></div> <p>Now, we have to repeat the placeholder text and wrap it with some tags:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Email<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textfield<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>input-email<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>input-email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>And of course, make the text visible when necessary:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">html.no-placeholder.no-js .item label</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h3 id="css-fallback-as-the-only-solution" style="position:relative;"><a href="#css-fallback-as-the-only-solution" aria-label="css fallback as the only solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS fallback as the only solution</h3> <p>Bypassing JavaScript polyfill and applying only CSS fallback is very similar to what I wrote previously. The only difference is this (no <code class="language-text">&lt;script></code> part here!):</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token comment">&lt;!--[if lte IE 9]> &lt;html lang="en" class="no-placeholder"> &lt;![endif]--></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>!--[if</span> <span class="token attr-name">gt</span> <span class="token attr-name">IE</span> <span class="token attr-name">9]</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>!--</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment">&lt;!--&lt;![endif]--></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">html.no-placeholder .item label</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p>I combined everything in the demo. Feel free to investigate the source code of the demo page and use the stuff in your projects. <strong>You should try the demo on browsers that do not support input placeholders</strong>, such as IE 9 or below.</p> <p><a href="https://v3.osvaldas.info/examples/placeholder-polyfill-with-password-support" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p><![CDATA[Forcing Description Text In a Newsletter]]>https://osvaldas.info/forcing-description-text-in-a-newsletterhttps://osvaldas.info/forcing-description-text-in-a-newsletterSun, 15 Mar 2015 00:00:00 GMT<p>I have recently sent a newsletter to the users of <del>Readerrr</del>, announcing a new feature (feed import). Besides the feature itself, it was not less important to send the newsletter correctly, i.e. to make it responsive, of course, and… to solve this problem (see the grayed description text):</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 390px; " data-url="../../images/blog/force-description-text-in-your-newsletter-9.jpg" data-alt="Readerrr newsletter"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.497536945812804%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQuD//xAAYEAACAwAAAAAAAAAAAAAAAAAAARARIf/aAAgBAQABBQKmbH//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAADAAMAAAAAAAAAAAAAAAAAASERMXH/2gAIAQEAAT8hwN1nYtU//9oADAMBAAIAAwAAABDwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAQEBAQEAAAAAAAAAAAAAAAERAEHx/9oACAEBAAE/EBsNXrTJ4GuOt//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/239e8a83556f20739c74e508b4cb319a/3466e/force-description-text-in-your-newsletter-9.webp 203w, /static/239e8a83556f20739c74e508b4cb319a/0e356/force-description-text-in-your-newsletter-9.webp 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/webp"> <source srcset="/static/239e8a83556f20739c74e508b4cb319a/37414/force-description-text-in-your-newsletter-9.jpg 203w, /static/239e8a83556f20739c74e508b4cb319a/ce2e0/force-description-text-in-your-newsletter-9.jpg 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/239e8a83556f20739c74e508b4cb319a/ce2e0/force-description-text-in-your-newsletter-9.jpg" alt="Readerrr newsletter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p><strong>This is a great example of bad <abbr title="User Experience">UX</abbr>.</strong> There are many email clients that provide an excerpt from the email text as a one-line (or a few) description (preview) under the email subject. Usually they take several first words from the email. If you checked the source code of the newsletters in your inbox, the first sentence in most of them would probably be (which means it is displayed as a description text) one of these:</p> <ul> <li><em>“Is this email not displaying correctly? View it in your browser”</em></li> <li><em>“View this email in your browser”</em></li> <li><em>“Read this issue on the Web”</em></li> </ul> <p>Such messages are completely <strong>useless</strong>. They do not provide any information on what this newsletter is about. Yes, there is a subject line, but that does not mean you should forget about the description. You could take an extra step in encouraging a user to read your newsletter if your subject was not that catchy.</p> <p>For example, Mail app for iOS dedicates only one line for the subject and two for the description text! Email/Gmail apps for Android have two and Yahoo.com has one line for the subject+description combination. Windows Phone and Microsoft Outlook dedicates one line for the subject and another for the description. There is so much potential you can exploit!</p> <p>Besides, <strong><em>“View this email in your browser”</em> in description text looks lame.</strong></p> <h2 id="solution" style="position:relative;"><a href="#solution" aria-label="solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Solution</h2> <p>The solution could not be more simple. What we want is to <strong>force a <em>silent</em> description</strong> <strong>message</strong> by not showing it in the newsletter. The range of CSS properties help to hide it in the newsletter across email clients:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">font-size</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span> Your catchy description text <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>Inserting this code snippet at the beginning of the document body makes sure the text is used as a description:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">font-size</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span> Your catchy description text <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div> <p>In the case of Readerrr, the result I got:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 390px; " data-url="../../images/blog/force-description-text-in-your-newsletter-10.jpg" data-alt="Readerrr newsletter"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.497536945812804%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQuD//xAAYEAACAwAAAAAAAAAAAAAAAAAAARARIf/aAAgBAQABBQKmbH//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAEBAAMBAAAAAAAAAAAAAAABABEhMVH/2gAIAQEAAT8hAXbY9xy//9oADAMBAAIAAwAAABDwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQACAwAAAAAAAAAAAAAAAAEAIRFB8P/aAAgBAQABPxBQLnbmcAgQDbP/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/10d4b7d370eefd3e004666b72f244259/3466e/force-description-text-in-your-newsletter-10.webp 203w, /static/10d4b7d370eefd3e004666b72f244259/0e356/force-description-text-in-your-newsletter-10.webp 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/webp"> <source srcset="/static/10d4b7d370eefd3e004666b72f244259/37414/force-description-text-in-your-newsletter-10.jpg 203w, /static/10d4b7d370eefd3e004666b72f244259/ce2e0/force-description-text-in-your-newsletter-10.jpg 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/10d4b7d370eefd3e004666b72f244259/ce2e0/force-description-text-in-your-newsletter-10.jpg" alt="Readerrr newsletter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>This is how the implementation of forced description looks on my end, with a little bit of flavor of PHP:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">font-size</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span> <span class="token prolog">&lt;?=substr( strip_tags( $description ?: $content ), 0, 200 )?></span><span class="token entity named-entity" title="&hellip;">&amp;hellip;</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>There is a custom field <code class="language-text">description</code> which I fill if I have anything catchy to write. Otherwise the content of the newsletter is truncated to 200 symbols and used instead.</p> <h2 id="examples-in-the-wild" style="position:relative;"><a href="#examples-in-the-wild" aria-label="examples in the wild permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Examples in the wild</h2> <p>I browsed my Mail app, and selected some good and bad examples (the first line is for sender’s name, second for the subject and the <strong>grayed lines are for the description</strong>):</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/force-description-text-in-your-newsletter-1.jpg" data-alt="Newsletters by JavaScript Weekly, Web Tools Weekly, Mobile Web Weekly, ThemeTrust"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 36.94581280788177%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQuD//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAACAwEAAAAAAAAAAAAAAAAAAREhMaH/2gAIAQEAAT8hi86NOaXRYf/aAAwDAQACAAMAAAAQgA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEBAAMBAAAAAAAAAAAAAAABEQAhMXH/2gAIAQEAAT8QsbZ3ZmCqwN65fRH3P//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/bc52a8370918421ac363415609f398f3/3466e/force-description-text-in-your-newsletter-1.webp 203w, /static/bc52a8370918421ac363415609f398f3/d7d9a/force-description-text-in-your-newsletter-1.webp 405w, /static/bc52a8370918421ac363415609f398f3/7df04/force-description-text-in-your-newsletter-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/bc52a8370918421ac363415609f398f3/37414/force-description-text-in-your-newsletter-1.jpg 203w, /static/bc52a8370918421ac363415609f398f3/32fb1/force-description-text-in-your-newsletter-1.jpg 405w, /static/bc52a8370918421ac363415609f398f3/a6335/force-description-text-in-your-newsletter-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/bc52a8370918421ac363415609f398f3/a6335/force-description-text-in-your-newsletter-1.jpg" alt="Newsletters by JavaScript Weekly, Web Tools Weekly, Mobile Web Weekly, ThemeTrust" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>This is what you should truly not do. Absolutely useless descriptions telling: <em>“hey, this is just another one issue/theme, nothing special”</em>.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/force-description-text-in-your-newsletter-2.jpg" data-alt="Newsletters from The Modern Desk and Twitter"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 17.24137931034483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAADABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3AUD/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABBQLIj//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABcQAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQEAAT8hijSpg//aAAwDAQACAAMAAAAQc8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhQVH/2gAIAQEAAT8QdDm30eRzYuiQf//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/d2eaca37eeb7c883d531c6d7c9c30de6/3466e/force-description-text-in-your-newsletter-2.webp 203w, /static/d2eaca37eeb7c883d531c6d7c9c30de6/d7d9a/force-description-text-in-your-newsletter-2.webp 405w, /static/d2eaca37eeb7c883d531c6d7c9c30de6/7df04/force-description-text-in-your-newsletter-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/d2eaca37eeb7c883d531c6d7c9c30de6/37414/force-description-text-in-your-newsletter-2.jpg 203w, /static/d2eaca37eeb7c883d531c6d7c9c30de6/32fb1/force-description-text-in-your-newsletter-2.jpg 405w, /static/d2eaca37eeb7c883d531c6d7c9c30de6/a6335/force-description-text-in-your-newsletter-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/d2eaca37eeb7c883d531c6d7c9c30de6/a6335/force-description-text-in-your-newsletter-2.jpg" alt="Newsletters from The Modern Desk and Twitter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Things gone messy here: repeating words and no clear message. <em>“the modern desk welcome hi there once again”</em> – what is that?</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/force-description-text-in-your-newsletter-3.jpg" data-alt="Newsletters from Youtube and Ello"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 17.24137931034483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAADABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3KFB/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAoj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAEBAQEAAAAAAAAAAAAAAAAAMQER/9oACAEBAAE/IeNqH//aAAwDAQACAAMAAAAQA8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEBAAMBAAAAAAAAAAAAAAABEQAhMUH/2gAIAQEAAT8QQLKVvcOnTXjgmP/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/958dffef07f1bf03ec7b8de9788df3c2/3466e/force-description-text-in-your-newsletter-3.webp 203w, /static/958dffef07f1bf03ec7b8de9788df3c2/d7d9a/force-description-text-in-your-newsletter-3.webp 405w, /static/958dffef07f1bf03ec7b8de9788df3c2/7df04/force-description-text-in-your-newsletter-3.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/958dffef07f1bf03ec7b8de9788df3c2/37414/force-description-text-in-your-newsletter-3.jpg 203w, /static/958dffef07f1bf03ec7b8de9788df3c2/32fb1/force-description-text-in-your-newsletter-3.jpg 405w, /static/958dffef07f1bf03ec7b8de9788df3c2/a6335/force-description-text-in-your-newsletter-3.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/958dffef07f1bf03ec7b8de9788df3c2/a6335/force-description-text-in-your-newsletter-3.jpg" alt="Newsletters from Youtube and Ello" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>An example of self-repeating instead of providing further information.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/force-description-text-in-your-newsletter-4.jpg" data-alt="Newsletters from Khoi Vinh and Kyle Steed"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 17.24137931034483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAADABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3AUD/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAoj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAYEAADAQEAAAAAAAAAAAAAAAAAAREhMf/aAAgBAQABPyGDW04P/9oADAMBAAIAAwAAABDzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAQEBAQEAAAAAAAAAAAAAABEBACFR/9oACAEBAAE/ELEtGN90B0gugz//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/9071803bbb7f61a3b787588f93661b7d/3466e/force-description-text-in-your-newsletter-4.webp 203w, /static/9071803bbb7f61a3b787588f93661b7d/d7d9a/force-description-text-in-your-newsletter-4.webp 405w, /static/9071803bbb7f61a3b787588f93661b7d/7df04/force-description-text-in-your-newsletter-4.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/9071803bbb7f61a3b787588f93661b7d/37414/force-description-text-in-your-newsletter-4.jpg 203w, /static/9071803bbb7f61a3b787588f93661b7d/32fb1/force-description-text-in-your-newsletter-4.jpg 405w, /static/9071803bbb7f61a3b787588f93661b7d/a6335/force-description-text-in-your-newsletter-4.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/9071803bbb7f61a3b787588f93661b7d/a6335/force-description-text-in-your-newsletter-4.jpg" alt="Newsletters from Khoi Vinh and Kyle Steed" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Almost there, but the mix with <em>“view this email in your browser”</em> ruins the description.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 390px; " data-url="../../images/blog/force-description-text-in-your-newsletter-5.jpg" data-alt="Exposure newsletter"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.497536945812804%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQuD//xAAXEAADAQAAAAAAAAAAAAAAAAABEBEh/9oACAEBAAEFAobq/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQEABj8Cr//EABcQAQADAAAAAAAAAAAAAAAAABEAEGH/2gAIAQEAAT8hBKar/9oADAMBAAIAAwAAABBzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQADAQEAAAAAAAAAAAAAAAEAESExUf/aAAgBAQABPxAcHRxbIb1Z4ZDk/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/23d853983787a1582583209713980505/3466e/force-description-text-in-your-newsletter-5.webp 203w, /static/23d853983787a1582583209713980505/0e356/force-description-text-in-your-newsletter-5.webp 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/webp"> <source srcset="/static/23d853983787a1582583209713980505/37414/force-description-text-in-your-newsletter-5.jpg 203w, /static/23d853983787a1582583209713980505/ce2e0/force-description-text-in-your-newsletter-5.jpg 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/23d853983787a1582583209713980505/ce2e0/force-description-text-in-your-newsletter-5.jpg" alt="Exposure newsletter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>The first word “Exposure” is taken from the value of <code class="language-text">alt</code> attribute of the first image (logo) in the newsletter. Forcing a custom <strong>semantic</strong> description would fix that.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 390px; " data-url="../../images/blog/force-description-text-in-your-newsletter-6.jpg" data-alt="Newsletter from Jonnie Hallman"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.497536945812804%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQuD//xAAXEAADAQAAAAAAAAAAAAAAAAAAEBEh/9oACAEBAAEFAoav/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAARAx/9oACAEBAAY/AqX/AP/EABcQAQEBAQAAAAAAAAAAAAAAAAEAMXH/2gAIAQEAAT8hWx0xl//aAAwDAQACAAMAAAAQcA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhMXHR4f/aAAgBAQABPxBZx23h9iaBHB3Lgttn/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/b0290d77b48d47ef766916e1ef46378d/3466e/force-description-text-in-your-newsletter-6.webp 203w, /static/b0290d77b48d47ef766916e1ef46378d/0e356/force-description-text-in-your-newsletter-6.webp 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/webp"> <source srcset="/static/b0290d77b48d47ef766916e1ef46378d/37414/force-description-text-in-your-newsletter-6.jpg 203w, /static/b0290d77b48d47ef766916e1ef46378d/ce2e0/force-description-text-in-your-newsletter-6.jpg 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/b0290d77b48d47ef766916e1ef46378d/ce2e0/force-description-text-in-your-newsletter-6.jpg" alt="Newsletter from Jonnie Hallman" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Forcing a custom description text would allow to use all of the space.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/force-description-text-in-your-newsletter-7.jpg" data-alt="Newsletters from Smashing Magazine, HTML5 Weekly, A Book Apart, Segment"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 36.94581280788177%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAduQsD//xAAWEAADAAAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCiP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABkQAAIDAQAAAAAAAAAAAAAAAAABESGhMf/aAAgBAQABPyGLdaNXMacH/9oADAMBAAIAAwAAABADz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAAMAAAAAAAAAAAAAAAEAESExYXH/2gAIAQEAAT8Qy173SWqHwaVnQnrc/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/4e1eafbbc2e49954cf2fa5efb4212f02/3466e/force-description-text-in-your-newsletter-7.webp 203w, /static/4e1eafbbc2e49954cf2fa5efb4212f02/d7d9a/force-description-text-in-your-newsletter-7.webp 405w, /static/4e1eafbbc2e49954cf2fa5efb4212f02/7df04/force-description-text-in-your-newsletter-7.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/4e1eafbbc2e49954cf2fa5efb4212f02/37414/force-description-text-in-your-newsletter-7.jpg 203w, /static/4e1eafbbc2e49954cf2fa5efb4212f02/32fb1/force-description-text-in-your-newsletter-7.jpg 405w, /static/4e1eafbbc2e49954cf2fa5efb4212f02/a6335/force-description-text-in-your-newsletter-7.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/4e1eafbbc2e49954cf2fa5efb4212f02/a6335/force-description-text-in-your-newsletter-7.jpg" alt="Newsletters from Smashing Magazine, HTML5 Weekly, A Book Apart, Segment" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Great examples of semantic descriptions. Looks solid. Most importantly I can smell what is in these newsletters! “ARIA in HTML5” was an interesting article to read.</p> <hr> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 390px; " data-url="../../images/blog/force-description-text-in-your-newsletter-8.jpg" data-alt="Quora Digest newsletter"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.497536945812804%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2qCwf//EABgQAAIDAAAAAAAAAAAAAAAAAAABEBEh/9oACAEBAAEFAqZsf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAAAEQMf/aAAgBAQAGPwKl/wD/xAAZEAACAwEAAAAAAAAAAAAAAAAAARExccH/2gAIAQEAAT8hc3Q2Kj//2gAMAwEAAgADAAAAEIAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRAAAwEBAQAAAAAAAAAAAAAAAAERMSHR/9oACAEBAAE/EOitKkIy8IuLp//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/9cfce04cef1b7f2942a52e321fd7cff9/3466e/force-description-text-in-your-newsletter-8.webp 203w, /static/9cfce04cef1b7f2942a52e321fd7cff9/0e356/force-description-text-in-your-newsletter-8.webp 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/webp"> <source srcset="/static/9cfce04cef1b7f2942a52e321fd7cff9/37414/force-description-text-in-your-newsletter-8.jpg 203w, /static/9cfce04cef1b7f2942a52e321fd7cff9/ce2e0/force-description-text-in-your-newsletter-8.jpg 390w" sizes="(max-width: 390px) 100vw, 390px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/9cfce04cef1b7f2942a52e321fd7cff9/ce2e0/force-description-text-in-your-newsletter-8.jpg" alt="Quora Digest newsletter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>My favorite example is from Quora: a question in the subject line and an answer in the description. Perfection!</p><![CDATA[Caching SVG Sprite in localStorage]]>https://osvaldas.info/caching-svg-sprite-in-localstoragehttps://osvaldas.info/caching-svg-sprite-in-localstorageThu, 05 Mar 2015 00:00:00 GMT<p>There are two ways of using SVG in HTML via <code class="language-text">&lt;use></code>: with external source and without it. “The <em>use</em> element takes nodes from within the SVG document, and duplicates them somewhere else” – <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use" target="_blank" rel="nofollow noopener noreferrer">MDN</a>. In the <strong>first</strong> case, the SVG graphics insertion (duplication) usually looks like this:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sprite.svg#cart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span></code></pre></div> <p>The good: the file <code class="language-text">sprite.svg</code> will be cached by the browser. The bad: this technique does not work in IE11 and below (luckily, there is a <a href="https://github.com/jonathantneal/svg4everybody" target="_blank" rel="nofollow noopener noreferrer">JS fallback</a> solution); you have to repeat the file name and the path to it on every insertion.</p> <p>The <strong>second</strong> case, which is the topic of this post, is about using the locally defined (inlined) SVG graphics. Note that I use <code class="language-text">&lt;symbol></code> when defining SVG. This means it is enough to set <code class="language-text">viewbox</code> once instead of doing this on every SVG insertion. Also, the SVG definitions should be placed above the insertions (<code class="language-text">&lt;use></code>), otherwise this will not work in some browsers:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-cart<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 50 50<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#svg-cart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre></div> <p>The good: it works in IE9 and above; you do not need to repeat the file name and the path (which is usually pretty long in real-life scenarios) to it on every image insertion:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- this is much nicer --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#svg-cart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- than this --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>theme/something/assets/img/sprite.svg#cart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span></code></pre></div> <p>The bad: SVG graphics (definitions) is a part of the document and so it is not cached by the browser as a separate subject, which means it is not reusable across the pages.</p> <p><strong>What if you have a lot of SVG graphics, like 100 KB in size?</strong> It means that every page of your website will have an additional 100 KB because idealistically this amount can be cached and reused. For example, if there was a cellular data user who browsed 11 different pages on your website, they would waste 1 MB – this is bad for performance. So, can we cache the SVG sprite and at the same time avoid referencing an external file on every image insertion?</p> <h2 id="localstorage" style="position:relative;"><a href="#localstorage" aria-label="localstorage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>localStorage</h2> <p>Yes! ​localStorage enables web pages to store the data within the user’s browser. The storage limit is usually 5 MB per domain. This is way more than enough for storing SVG sprites.</p> <p>I wrote a tiny piece of JavaScript code to handle this part (~444 bytes minified and gziped, no dependencies). Here is how it works:</p> <ul> <li>On the very first load of the website it: <ol> <li>Reads the contents of a SVG file;</li> <li>Inserts the data into the document;</li> <li>Puts the data into localStorage.</li> </ol> </li> <li>On every following load it: <ol> <li>Reads the data from localStorage;</li> <li>Inserts the SVG data in the document.</li> </ol> </li> </ul> <p>If localStorage is not supported, is disabled or overfilled, the script still reads the contents of a SVG file and inserts the data in the document.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window<span class="token punctuation">,</span> document</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">'use strict'</span><span class="token punctuation">;</span> <span class="token keyword">var</span> file <span class="token operator">=</span> <span class="token string">'img/svg.html'</span><span class="token punctuation">,</span> revision <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>document<span class="token punctuation">.</span>createElementNS <span class="token operator">||</span> <span class="token operator">!</span>document<span class="token punctuation">.</span><span class="token function">createElementNS</span><span class="token punctuation">(</span><span class="token string">'http://www.w3.org/2000/svg'</span><span class="token punctuation">,</span> <span class="token string">'svg'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>createSVGRect<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">var</span> isLocalStorage <span class="token operator">=</span> <span class="token string">'localStorage'</span> <span class="token keyword">in</span> window <span class="token operator">&amp;&amp;</span> window<span class="token punctuation">[</span><span class="token string">'localStorage'</span><span class="token punctuation">]</span> <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">,</span> request<span class="token punctuation">,</span> data<span class="token punctuation">,</span> <span class="token function-variable function">insertIT</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">insertAdjacentHTML</span><span class="token punctuation">(</span><span class="token string">'afterbegin'</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">insert</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span>body<span class="token punctuation">)</span> <span class="token function">insertIT</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'DOMContentLoaded'</span><span class="token punctuation">,</span> insertIT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>isLocalStorage <span class="token operator">&amp;&amp;</span> localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'inlineSVGrev'</span><span class="token punctuation">)</span> <span class="token operator">==</span> revision<span class="token punctuation">)</span> <span class="token punctuation">{</span> data <span class="token operator">=</span> localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'inlineSVGdata'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> request<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'GET'</span><span class="token punctuation">,</span> file<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> request<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>status <span class="token operator">>=</span> <span class="token number">200</span> <span class="token operator">&amp;&amp;</span> request<span class="token punctuation">.</span>status <span class="token operator">&lt;</span> <span class="token number">400</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> data <span class="token operator">=</span> request<span class="token punctuation">.</span>responseText<span class="token punctuation">;</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>isLocalStorage<span class="token punctuation">)</span> <span class="token punctuation">{</span> localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'inlineSVGdata'</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span> localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'inlineSVGrev'</span><span class="token punctuation">,</span> revision<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>You can safely place this code anywhere in the document as the SVG file scanning works asynchronously. If you put the code at the <em>head</em> of the document, the icons will load a little bit faster. Test it to see what is best for you.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/caching-svg-sprite-in-localstorage-1.jpg" data-alt="Caching SVG Sprite in localStorage"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 39.408866995073886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2aFB/8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEh/9oACAEBAAEFAqx//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAABAx/9oACAEBAAY/Air/xAAYEAACAwAAAAAAAAAAAAAAAAAAkRARYf/aAAgBAQABPyFBW1H/2gAMAwEAAgADAAAAEPvP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABsQAQACAgMAAAAAAAAAAAAAABEAASFBMWHB/9oACAEBAAE/ELzVQ9nEb4oG3uf/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/9470e4ff44027da51f6565cf67cb603a/3466e/caching-svg-sprite-in-localstorage-1.webp 203w, /static/9470e4ff44027da51f6565cf67cb603a/d7d9a/caching-svg-sprite-in-localstorage-1.webp 405w, /static/9470e4ff44027da51f6565cf67cb603a/7df04/caching-svg-sprite-in-localstorage-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/9470e4ff44027da51f6565cf67cb603a/37414/caching-svg-sprite-in-localstorage-1.jpg 203w, /static/9470e4ff44027da51f6565cf67cb603a/32fb1/caching-svg-sprite-in-localstorage-1.jpg 405w, /static/9470e4ff44027da51f6565cf67cb603a/a6335/caching-svg-sprite-in-localstorage-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/9470e4ff44027da51f6565cf67cb603a/a6335/caching-svg-sprite-in-localstorage-1.jpg" alt="Caching SVG Sprite in localStorage" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h3 id="file--revision" style="position:relative;"><a href="#file--revision" aria-label="file revision permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>File &#x26; Revision</h3> <p>There are two lines you need to configure on your own:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> file <span class="token operator">=</span> <span class="token string">'img/svg.html'</span><span class="token punctuation">,</span> revision <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span></code></pre></div> <p><code class="language-text">file</code> is the path to the SVG file. It can be <code class="language-text">*.svg</code> file but personally I do not use the sprite file as a SVG file, I treat it as a part of my HTML document. My <a href="https://v3.osvaldas.info/examples/caching-svg-sprite-in-localstorage/img/svg.html" target="_blank" rel="nofollow noopener noreferrer"><code class="language-text">svg.html</code></a> usually looks this:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-plane<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 510 510<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-close<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 357 357<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-fav<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 510 510<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-share<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 459 459<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-cart<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 510 510<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>symbol</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg-tick<span class="token punctuation">"</span></span> <span class="token attr-name">viewbox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 510 510<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>symbol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span></code></pre></div> <p><code class="language-text">revision</code> is a version number of <code class="language-text">svg.html</code> file. It tells the script when it should scan the file for changes and update the old SVG data which is cached in localStorage to a new one. For example, if you update the SVG file, you also have to change the value of <code class="language-text">revision</code> variable in order to make the changes visible on the website.</p> <p>You can do this manually or you can automate this via PHP (Perl, Python or any other server-side scripting language). If you have the JavaScript code placed directly into your HTML code (which is located in <code class="language-text">*.php</code> file), you can use PHP function which returns file modification time (as a Unix timestamp):</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">revision <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">=</span><span class="token function">filemtime</span><span class="token punctuation">(</span> <span class="token string">'img/svg.html'</span> <span class="token punctuation">)</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">;</span></code></pre></div> <p>If you have the script in a separate JavaScript file, i.e. <code class="language-text">common.js</code>, you can use a global JavaScript variable:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">revision <span class="token operator">=</span> <span class="token constant">INLINE_SVG_REVISION</span><span class="token punctuation">;</span></code></pre></div> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token keyword">var</span> <span class="token constant">INLINE_SVG_REVISION</span> <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">=</span><span class="token function">filemtime</span><span class="token punctuation">(</span> <span class="token string">'img/svg.html'</span> <span class="token punctuation">)</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>common.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div> <h3 id="no-svg-support" style="position:relative;"><a href="#no-svg-support" aria-label="no svg support permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>No SVG support?</h3> <p>If a browser does not support SVG, we can rely on raster images and JavaScript fallback. We need <code class="language-text">data-img</code> attribute for specifying the location of raster image, for example:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#svg-cart<span class="token punctuation">"</span></span> <span class="token attr-name">data-img</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img/cart.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></code></pre></div> <p>The fallback will convert that line to this:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img/cart.png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></code></pre></div> <p>Then, if you do not use <code class="language-text">html5shiv</code>, you have to register <code class="language-text">svg</code> and <code class="language-text">use</code> elements so that the fallback code can later operate them. This should be placed at the document <em>head</em>, best before the first <code class="language-text">&lt;script></code> occurence:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>document<span class="token punctuation">.</span>createElementNS <span class="token operator">||</span> <span class="token operator">!</span>document<span class="token punctuation">.</span><span class="token function">createElementNS</span><span class="token punctuation">(</span><span class="token string">'http://www.w3.org/2000/svg'</span><span class="token punctuation">,</span> <span class="token string">'svg'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>createSVGRect<span class="token punctuation">)</span> <span class="token punctuation">{</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'svg'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'use'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Finally, the fallback itself – it should be placed at the end of the document (I borrowed some lines from <a href="https://github.com/jonathantneal/svg4everybody" target="_blank" rel="nofollow noopener noreferrer">svg4everybody</a>):</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window<span class="token punctuation">,</span> document</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span>createElementNS <span class="token operator">&amp;&amp;</span> document<span class="token punctuation">.</span><span class="token function">createElementNS</span><span class="token punctuation">(</span><span class="token string">'http://www.w3.org/2000/svg'</span><span class="token punctuation">,</span> <span class="token string">'svg'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>createSVGRect<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">var</span> uses <span class="token operator">=</span> document<span class="token punctuation">.</span>getElementsByTagName <span class="token string">'use'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> use<span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token punctuation">(</span>use <span class="token operator">=</span> uses<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> svg <span class="token operator">=</span> use<span class="token punctuation">.</span>parentNode<span class="token punctuation">,</span> img <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Image</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> img<span class="token punctuation">.</span>src <span class="token operator">=</span> use<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'data-img'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> svg<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">replaceChild</span><span class="token punctuation">(</span>img<span class="token punctuation">,</span> svg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="javascript-disabled" style="position:relative;"><a href="#javascript-disabled" aria-label="javascript disabled permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript disabled?</h3> <p>The worst part about this technique is that you cannot get SVG sprite functioning if JavaScript is disabled in user’s browser. But, like in “no SVG support” case, we can rely on raster images, for example:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#svg-cart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>use</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img/cart.png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p>I combined everything in the demo. Feel free to investigate the source code of the demo page and use the stuff in your projects where you have lots of SVG icons:</p> <p><a href="https://v3.osvaldas.info/examples/caching-svg-sprite-in-localstorage/" target="_blank" rel="nofollow noopener noreferrer">Demo</a></p> <p>P.S. In this article I focused only on the technique of caching SVG and I did not show how to make things <em>accessible</em> in code examples. As for real-life projects I encourage you considering these <a href="https://github.com/jonathantneal/svg4everybody#readability-and-accessibility" target="_blank" rel="nofollow noopener noreferrer">SVG accessibility tips</a>.</p><![CDATA[Auto-Hide Sticky Header]]>https://osvaldas.info/auto-hide-sticky-headerhttps://osvaldas.info/auto-hide-sticky-headerWed, 14 Jan 2015 00:00:00 GMT<p>Auto-hide sticky header is a shot that shoots down two rabbits: <strong>makes site navigation easily accessible</strong> anywhere on the page and <strong>saves content space</strong> at the same. My client, Easy Shine, was happy to have this type of header for their website. I've also implemented this technique <em>here</em>, on my website (you can see it when the width of your viewport is below 768 px).</p> <h2 id="making-header-sticky" style="position:relative;"><a href="#making-header-sticky" aria-label="making header sticky permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Making header sticky</h2> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>header<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>banner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.header</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 7.5em<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 1000<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>A little bit of <em>obvious</em> HTML/CSS coding here, which makes the header stick to the top of the page, independently of the page scroll position. Now, how do we auto-hide the header?</p> <h2 id="auto-hiding-header" style="position:relative;"><a href="#auto-hiding-header" aria-label="auto hiding header permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Auto-hiding header</h2> <p><em>Auto-hide</em> means hiding the header automatically when a user starts scrolling down the page and bringing the header back when a user might need it: they reach the bottom of the page or start scrolling up. There are at least two manners of hiding a header: reactive and lazy.</p> <h3 id="reactive" style="position:relative;"><a href="#reactive" aria-label="reactive permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Reactive</h3> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/116655295" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>It is when the header directly and <strong>instantly reacts to the page scroll</strong>. This manner may be a good small detail from the perspective of user experience because of the feel it brings. However it has a downside: it completely depends on JavaScript and because of the manner’s nature, we cannot use JS event throttling (calling a handler function only once in a custom period of time). In principle, that means making calculations on every scroll event and quite uselessly loading CPU. Luckily, in most cases this more a theoretical, but not a practical problem because the calculations are quite insignificant.</p> <p>The JS algorithm changes the value of CSS property <code class="language-text">top</code> when page scroll is performed:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">//...</span> window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//...</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollCurrent <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// scrolled to the very top; element sticks to the top</span> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>top <span class="token operator">=</span> <span class="token string">'0px'</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollDiff <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// scrolled up; element slides in</span> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>top <span class="token operator">=</span> <span class="token punctuation">(</span>elTop <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token number">0</span> <span class="token operator">:</span> elTop<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'px'</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollDiff <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// scrolled down</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollCurrent <span class="token operator">+</span> wHeight <span class="token operator">>=</span> dHeight <span class="token operator">-</span> elHeight<span class="token punctuation">)</span> <span class="token comment">// scrolled to the very bottom; element slides in</span> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>top <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>elTop <span class="token operator">=</span> wScrollCurrent <span class="token operator">+</span> wHeight <span class="token operator">-</span> dHeight<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">0</span> <span class="token operator">?</span> elTop <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'px'</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token comment">// scrolled down; element slides out</span> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>top <span class="token operator">=</span> <span class="token punctuation">(</span> Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span> elTop <span class="token punctuation">)</span> <span class="token operator">></span> elHeight <span class="token operator">?</span> <span class="token operator">-</span>elHeight <span class="token operator">:</span> elTop <span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'px'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//...</span></code></pre></div> <h3 id="lazy" style="position:relative;"><a href="#lazy" aria-label="lazy permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Lazy</h3> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/116655294" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>This one, however, may not have that feeling of responsiveness, which depends a lot on JavaScript event throttling interval. Anyway, the manner is much more CPU-friendly and the <strong>header animation is based on CSS</strong>, which means we can make a use of our imagination.</p> <p>Differently from the previous option, here the JavaScript part does not change CSS properties directly. It adds and removes CSS class name <code class="language-text">header--hidden</code>:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">//...</span> window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token function">throttle</span><span class="token punctuation">(</span> throttleTimeout<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//...</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollCurrent <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// scrolled to the very top; element sticks to the top</span> <span class="token function">removeElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollDiff <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> <span class="token function">hasElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// scrolled up; element slides in</span> <span class="token function">removeElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollDiff <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// scrolled down</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollCurrent <span class="token operator">+</span> wHeight <span class="token operator">>=</span> dHeight <span class="token operator">&amp;&amp;</span> <span class="token function">hasElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// scrolled to the very bottom; element slides in</span> <span class="token function">removeElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">else</span> <span class="token comment">// scrolled down; element slides out</span> <span class="token function">addElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassHidden<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//...</span></code></pre></div> <p>In CSS we define what the class name means:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.header</span> <span class="token punctuation">{</span> <span class="token property">-webkit-transition-duration</span><span class="token punctuation">:</span> .5s<span class="token punctuation">;</span> <span class="token property">transition-duration</span><span class="token punctuation">:</span> .5s<span class="token punctuation">;</span> <span class="token property">-webkit-transition-timing-function</span><span class="token punctuation">:</span> <span class="token function">cubic-bezier</span><span class="token punctuation">(</span>0.215<span class="token punctuation">,</span> 0.610<span class="token punctuation">,</span> 0.355<span class="token punctuation">,</span> 1.000<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transition-timing-function</span><span class="token punctuation">:</span> <span class="token function">cubic-bezier</span><span class="token punctuation">(</span>0.215<span class="token punctuation">,</span> 0.610<span class="token punctuation">,</span> 0.355<span class="token punctuation">,</span> 1.000<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">-webkit-transition-property</span><span class="token punctuation">:</span> -webkit-transform<span class="token punctuation">;</span> <span class="token property">transition-property</span><span class="token punctuation">:</span> transform<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.header--hidden</span> <span class="token punctuation">{</span> <span class="token property">-webkit-transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">-ms-transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="an-extra-state-for-header" style="position:relative;"><a href="#an-extra-state-for-header" aria-label="an extra state for header permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>An extra state for header</h2> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/116655292" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>Sometimes, especially on homepage, it might be useful to have a bigger header when at the top of the page in order to <strong>grab visitor's attention</strong>. We need an additional CSS class for controlling header height:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.header--narrow</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 5em<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Whereas JavaScript part controls the usage of the newly defined CSS class name – it adds the class name when a page scroll starts and removes when a user reaches the page top.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// ...</span> window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token function">throttle</span><span class="token punctuation">(</span>throttleTimeout<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token keyword">if</span><span class="token punctuation">(</span>wScrollCurrent <span class="token operator">></span> elNarrowOffset<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// toggles "narrow" classname</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">hasElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassNarrow<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">addElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassNarrow<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token function">removeElementClass</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> elClassNarrow<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ...</span></code></pre></div> <h2 id="examples" style="position:relative;"><a href="#examples" aria-label="examples permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Examples</h2> <p>The source of the demo consists of both pure-JavaScript (IE9+ compatible) and jQuery-dependent versions of the code. Feel free to investigate and use it.</p> <p><a href="https://v3.osvaldas.info/examples/auto-hide-sticky-header" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p><![CDATA[Lazy-loading Google Maps]]>https://osvaldas.info/lazy-loading-google-mapshttps://osvaldas.info/lazy-loading-google-mapsThu, 11 Dec 2014 00:00:00 GMT<p>I would call this year <em>a year of web performance</em>. The very useful web performance solutions developed by people who make websites. The cult of website performance analyzers. The rise of mobile data users. The releases of faster mobile browsers. The better raking on Google for <em>faster</em> websites. The conferences dedicated to web performance. All of these affected my work and web performance has become a core factor. I started questioning every element of a website: <em>can I optimize this?</em></p> <p>For my latest <em>responsive</em> freelance project there was a contact page where I had to display several Google Maps instances on a single page. I believe you can see where I am heading to: <strong>what if</strong> a user is not going to scroll down the page because the very first map is what a user was looking for? <strong>What if</strong> the user is accessing the page on a smartphone under the cellular internet connection?</p> <p>The easiest way would be unconditionally loading a script file and map instances at once. But that would be a very very wrong way. The right way is to <strong>lazy-load</strong> the script file and map instances one by one.</p> <p><img src="/ee98f5e72fc89bae75d623dac52c3770/lazy-loading-google-maps-1.gif" alt="Lazy Loading Google Maps"></p> <h2 id="jquery-plugin" style="position:relative;"><a href="#jquery-plugin" aria-label="jquery plugin permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>jQuery plugin</h2> <p>As usual, I have developed a tiny <strong>jQuery plugin, which just loads the maps and then gives away the full control of the loaded ones to you:</strong></p> <ul> <li><strong><a href="https://v3.osvaldas.info/examples/lazy-loading-google-maps/jquery.lazy-load-google-maps.js" target="_blank" rel="nofollow noopener noreferrer">jquery.lazy-load-google-maps.js</a></strong> (uncompressed; 2 KB);</li> <li><strong><a href="https://v3.osvaldas.info/examples/lazy-loading-google-maps/jquery.lazy-load-google-maps.min.js" target="_blank" rel="nofollow noopener noreferrer">jquery.lazy-load-google-maps.min.js</a></strong> (minified; 1 KB).</li> </ul> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lazyLoadGoogleMaps</span><span class="token punctuation">(</span><span class="token punctuation">[</span>options<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The plugin accepts some options. The defaults are:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lazyLoadGoogleMaps</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// just an API key if you have one</span> <span class="token literal-property property">libraries</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// libraries to load, i.e. 'geometry,places'</span> <span class="token literal-property property">signed_in</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// sign-in on a map enabled/disabled</span> <span class="token literal-property property">language</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// set language, i.e. 'en', 'en-GB'</span> <span class="token literal-property property">region</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// set region, i.e. 'GB'</span> <span class="token literal-property property">callback</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token comment">/* a callback function called within every map instance initiation; there are two variables that are passed as parameters to the function: function( container, map ) * container - the container element which has a selector 'selector' (see the first line) * map - the map instance which is a part of Google Maps API */</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>It also has two helper methods, which I’ll explain later in the post:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> $instance <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lazyLoadGoogleMaps</span><span class="token punctuation">(</span><span class="token punctuation">[</span>options<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $instance<span class="token punctuation">.</span><span class="token function">debounce</span><span class="token punctuation">(</span>duration<span class="token punctuation">,</span> fn<span class="token punctuation">)</span><span class="token punctuation">;</span> $instance<span class="token punctuation">.</span><span class="token function">throttle</span><span class="token punctuation">(</span>duration<span class="token punctuation">,</span> fn<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="how-the-plugin-works" style="position:relative;"><a href="#how-the-plugin-works" aria-label="how the plugin works permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>How the plugin works?</h2> <p>On every scroll and browser resize interval the plugin checks whether there are any maps in the viewport to be displayed. If yes, it loads (if it wasn’t loaded before) Google Maps API script file and then initiates the corresponding map instances. Finally, there’s a callback function which is called within every map instance initiation so that you can continue doing whatever you need with your maps.</p> <p>Let’s say you have five maps with different locations, and you need to mark them and center the view. The most simple <em>practical</em> use of the plugin would look like this:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>google-map<span class="token punctuation">"</span></span> <span class="token attr-name">data-lat</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40.7056258<span class="token punctuation">"</span></span> <span class="token attr-name">data-lng</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-73.97968<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>google-map<span class="token punctuation">"</span></span> <span class="token attr-name">data-lat</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>51.5072113<span class="token punctuation">"</span></span> <span class="token attr-name">data-lng</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-0.1144521<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>google-map<span class="token punctuation">"</span></span> <span class="token attr-name">data-lat</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>31.2243489<span class="token punctuation">"</span></span> <span class="token attr-name">data-lng</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>121.4767528<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>google-map<span class="token punctuation">"</span></span> <span class="token attr-name">data-lat</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>48.8588589<span class="token punctuation">"</span></span> <span class="token attr-name">data-lng</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2.3470599<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>google-map<span class="token punctuation">"</span></span> <span class="token attr-name">data-lat</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35.7090711<span class="token punctuation">"</span></span> <span class="token attr-name">data-lng</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>139.7321219<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.lazy-load-google-maps.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.google-map'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lazyLoadGoogleMaps</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">container<span class="token punctuation">,</span> map</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $container <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>container<span class="token punctuation">)</span><span class="token punctuation">,</span> center<span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">google<span class="token punctuation">.</span>maps<span class="token punctuation">.</span>LatLng</span><span class="token punctuation">(</span>$container<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'data-lat'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $container<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'data-lng'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> map<span class="token punctuation">.</span><span class="token function">setOptions</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">zoom</span><span class="token operator">:</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token literal-property property">center</span><span class="token operator">:</span> center <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">new</span> <span class="token class-name">google<span class="token punctuation">.</span>maps<span class="token punctuation">.</span>Marker</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">position</span><span class="token operator">:</span> center<span class="token punctuation">,</span> <span class="token literal-property property">map</span><span class="token operator">:</span> map <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p>The <code class="language-text">callback</code> function will be called <strong>5 times</strong> here and will walk through the each map. Using HTML <code class="language-text">[data-*]</code> attributes to specify the coordinates and later retrieving them with jQuery’s <code class="language-text">$container.attr( 'data-*' )</code> makes the maps easily maintainable. You can pass more information in this manner according to your needs. So basically, you can fully use Google Maps API inside the callback function. You can also store the map instances in an array and use the API outside the callback function. This is the next example…</p> <h2 id="responsive-maps" style="position:relative;"><a href="#responsive-maps" aria-label="responsive maps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Responsive maps</h2> <p><img src="/ece45786c844c4c9f27baf4b33b9da6a/lazy-loading-google-maps-2.gif" alt="Lazy Loading Google Maps"></p> <p>Of course, your map containers are <em>fluid</em> because you design responsive websites. But <strong>the problem</strong> is that when a container resizes, the map looses focus. When the container becomes smaller, the location mark usually hides; when larger, you usually find the mark in the part of the container where you don’t want it to be. Google Maps API does not take care of this by default, so we have to.</p> <p>Let’s assign the information of the default center for each map instance and, as mentioned previously, store the map instances in an array. Finally, during each browser resize interval, we walk through the map instances and set the map center to the default value.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> $window <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// variable cache for a better performance</span> mapInstances <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// stack of map instances</span> $pluginInstance <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.google-map'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">lazyLoadGoogleMaps</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">container<span class="token punctuation">,</span> map</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $container <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>container<span class="token punctuation">)</span><span class="token punctuation">,</span> center <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">google<span class="token punctuation">.</span>maps<span class="token punctuation">.</span>LatLng</span><span class="token punctuation">(</span>$container<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'data-lat'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $container<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'data-lng'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $<span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> <span class="token string">'center'</span><span class="token punctuation">,</span> center<span class="token punctuation">)</span><span class="token punctuation">;</span> mapInstances<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>map<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $window<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> $pluginInstance<span class="token punctuation">.</span><span class="token function">debounce</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $<span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span>mapInstances<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setCenter</span><span class="token punctuation">(</span>$<span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string">'center'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Wondering what <code class="language-text">$pluginInstance.debounce()</code> does here? The next part of the post is exactly about the importance of throttling and deboucing events in JavaScript…</p> <h2 id="throttling--debouncing" style="position:relative;"><a href="#throttling--debouncing" aria-label="throttling debouncing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Throttling &#x26; debouncing</h2> <p>Have you ever experienced a browser crash when flipping your smartphone/tablet? There are a lot of chances it has happened because of the poorly developed website…</p> <p><strong>Throttling.</strong> Simply put, the number 250 (0.25 seconds) means that during the page scroll, the script makes 4 calculations per second at most. Without throttling, it would be ~20 times per second, but there’s totally no need to calculate it that often and, therefore, to put so much weight on user’s CPU.</p> <p><strong>Debouncing.</strong> The number 1000 (1 second) means that the calculation will only be performed 1 second after the last browser window <em>resize</em> action. For example, if you keep resizing the window for several seconds by moving the mouse around, no calculations will be performed during that time. The action is only performed after you release the mouse button and 1 second passes – the code then <em>thinks</em> that you have finished resizing the window. There’s totally no need for calculations during the resize process.</p> <p>My advice: if you have never implemented throttling and debouncing before, you should check your codes and update where necessary. Be <em>responsible</em>.</p> <p>Both throttling and debouncing are used inside the plugin; therefore, I 've made them available as plugin methods so you can use them in order to optimize your maps experience.</p> <h2 id="efficiency" style="position:relative;"><a href="#efficiency" aria-label="efficiency permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Efficiency</h2> <p>Finally, let’s see how we can save the mobile data plans of our visitors. The hypothetical situation I mentioned previously: a user opens the contact page on his smartphone connected to cellular network, sees the first map and does not scroll down the page because the first map is what was looking for. If the contact page was like in my demo, the whole download size would be <strong>2.58 MB</strong>. Without lazy-load it could have been <strong>6.44 MB</strong>. In this case we save <strong>3.86 MB</strong> and the mental impact was that the page was loaded <strong>2.5 times faster</strong>. Way to go!</p> <p>The demo contains all of the features I described previously and there are a few user experience enriching additions.</p> <p><a href="https://v3.osvaldas.info/examples/lazy-loading-google-maps" target="_blank" rel="nofollow noopener noreferrer">Demo</a></p> <p>You can also contribute, follow the project on <a href="https://github.com/osvaldasvalutis/lazyLoadGoogleMaps.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a>.</p> <p>I am going to share more lazy-loading techniques, so be sure to subscribe for the updates! Thank you for reading.</p> <h2 id="changelog" style="position:relative;"><a href="#changelog" aria-label="changelog permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Changelog</h2> <ul> <li>The plugin now is on <a href="https://github.com/osvaldasvalutis/lazyLoadGoogleMaps.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a> too. Fixed the key issue: use <code class="language-text">key</code> instead of <code class="language-text">api_key</code>. (<em>2016-11-23</em>)</li> <li>Added new options: <code class="language-text">libraries</code>, <code class="language-text">signed_in</code>, <code class="language-text">language</code>, <code class="language-text">region</code>. (<em>2015-11-07</em>)</li> </ul><![CDATA[Keeping CSS short with currentColor]]>https://osvaldas.info/keeping-css-short-with-currentcolorhttps://osvaldas.info/keeping-css-short-with-currentcolorThu, 27 Nov 2014 00:00:00 GMT<p>Turns out <code class="language-text">currentColor</code> has been here for quite some time now, but I heard about it only a few months ago when I read Dudley Storey’s <a href="http://demosthenes.info/blog/908/The-First-CSS-Variable-currentColor" target="_blank" rel="nofollow noopener noreferrer">post</a>. He states that it is supported very well across the browsers (IE9+). This was enough for me to start using it in production. I was quite surprised how useful the keyword is: it helps to keep CSS code shorter and smarter.</p> <p>Before diving into practical usage examples, here is a short theory course. This is how MDN describes <code class="language-text">currentColor</code>:</p> <blockquote> <p>The <code class="language-text">currentColor</code> keyword represents the calculated value of the element’s <code class="language-text">color</code> property. It allows to make the color properties inherited by properties or child's element properties that do not inherit it by default.</p> </blockquote> <h2 id="svg" style="position:relative;"><a href="#svg" aria-label="svg permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>SVG</h2> <p>This is my favorite. Take a very common example on the Web – a button with SVG icon and a title in it. I have these here on my website too:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/keeping-css-short-with-currentcolor-1.jpg" data-alt="A button with SVG icon in it"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.70935960591133%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHvzRDS/wD/xAAWEAADAAAAAAAAAAAAAAAAAAAAASD/2gAIAQEAAQUCFP8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAgMAAAAAAAAAAAAAAAAAEBEAATH/2gAIAQEAAT8hvIxQ/9oADAMBAAIAAwAAABB3D//EABURAQEAAAAAAAAAAAAAAAAAABBB/9oACAEDAQE/EKf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABETEAECFx/9oACAEBAAE/EJ0xdSr7Gkm8AUBr/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/868c99716cb398a0d68e5ac83bed86e6/3466e/keeping-css-short-with-currentcolor-1.webp 203w, /static/868c99716cb398a0d68e5ac83bed86e6/d7d9a/keeping-css-short-with-currentcolor-1.webp 405w, /static/868c99716cb398a0d68e5ac83bed86e6/7df04/keeping-css-short-with-currentcolor-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/868c99716cb398a0d68e5ac83bed86e6/37414/keeping-css-short-with-currentcolor-1.jpg 203w, /static/868c99716cb398a0d68e5ac83bed86e6/32fb1/keeping-css-short-with-currentcolor-1.jpg 405w, /static/868c99716cb398a0d68e5ac83bed86e6/a6335/keeping-css-short-with-currentcolor-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/868c99716cb398a0d68e5ac83bed86e6/a6335/keeping-css-short-with-currentcolor-1.jpg" alt="A button with SVG icon in it" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Of course, you are a very responsible web designer, and you style <code class="language-text">:hover</code>, <code class="language-text">:focus</code>, <code class="language-text">:active</code> states of the button for a better interaction with a user. This is how your code usually looks like:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.button</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid #000<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:hover, .button:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:active</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button svg</span> <span class="token punctuation">{</span> <span class="token property">fill</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:hover svg, .button:focus svg</span> <span class="token punctuation">{</span> <span class="token property">fill</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:active svg</span> <span class="token punctuation">{</span> <span class="token property">fill</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Currently I am writing a front-end code for client’s e-commerce website which has a few different button designs. Moreover, there are anchors that have <code class="language-text">:visited</code> state styled in addition. And there are many more similar SVG usage cases (toolbars, etc.) where SVG has to have the color of the text. <code class="language-text">currentColor</code> helps to <strong>reduce the code twice:</strong></p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token comment">/* put this in your reset-normalize-defaults.css file */</span> <span class="token selector">svg</span> <span class="token punctuation">{</span> <span class="token property">fill</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* now you don't have to style SVG and border-color at all */</span> <span class="token selector">.button</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid currentColor<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:hover, .button:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.button:active</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="gradients" style="position:relative;"><a href="#gradients" aria-label="gradients permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Gradients</h2> <p>The keyword can be used anywhere where the value is the definition of the color, including gradients. In my <a href="/redesign">previous post</a> I talked a little about how I implemented that trendy hyperlink underlining:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/keeping-css-short-with-currentcolor-2.jpg" data-alt="Link underlines stylized with text-shadow and CSS gradients"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 26.108374384236456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3wUH/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABPyF//9oADAMBAAIAAwAAABDwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAIDAQAAAAAAAAAAAAAAAAERABBRcf/aAAgBAQABPxA9UR01/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/298bb470e58daf3bf043672c26e333ba/3466e/keeping-css-short-with-currentcolor-2.webp 203w, /static/298bb470e58daf3bf043672c26e333ba/d7d9a/keeping-css-short-with-currentcolor-2.webp 405w, /static/298bb470e58daf3bf043672c26e333ba/7df04/keeping-css-short-with-currentcolor-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/298bb470e58daf3bf043672c26e333ba/37414/keeping-css-short-with-currentcolor-2.jpg 203w, /static/298bb470e58daf3bf043672c26e333ba/32fb1/keeping-css-short-with-currentcolor-2.jpg 405w, /static/298bb470e58daf3bf043672c26e333ba/a6335/keeping-css-short-with-currentcolor-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/298bb470e58daf3bf043672c26e333ba/a6335/keeping-css-short-with-currentcolor-2.jpg" alt="Link underlines stylized with text-shadow and CSS gradients" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Usual CSS equivalent including interactive states:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">text-shadow</span><span class="token punctuation">:</span> 2px 0 0 #fff<span class="token punctuation">,</span> -2px 0 0 #fff<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span> left<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #000 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #000 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat-x<span class="token punctuation">;</span> <span class="token property">background-position</span><span class="token punctuation">:</span> 0 95%<span class="token punctuation">;</span> <span class="token property">-webkit-background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:hover, a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span> left<span class="token punctuation">,</span> #333 0%<span class="token punctuation">,</span> #333 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> #333 0%<span class="token punctuation">,</span> #333 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span> left<span class="token punctuation">,</span> #666 0%<span class="token punctuation">,</span> #666 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> #666 0%<span class="token punctuation">,</span> #666 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:visited</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #999<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span> left<span class="token punctuation">,</span> #999 0%<span class="token punctuation">,</span> #999 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> #999 0%<span class="token punctuation">,</span> #999 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p><code class="language-text">background-image</code> is responsible for the underline and it has the same color as the text. The code looks massive. Nevertheless, you usually do not limit yourself to one link color. In my personal experience there are at least three of them: general link color, grey, and white (when on dark background). This means 3x more of the code. But <code class="language-text">currentColor</code> does the magic again:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">text-shadow</span><span class="token punctuation">:</span> 2px 0 0 #fff<span class="token punctuation">,</span> -2px 0 0 #fff<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span> left<span class="token punctuation">,</span> currentColor 0%<span class="token punctuation">,</span> currentColor 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> currentColor 0%<span class="token punctuation">,</span> currentColor 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat-x<span class="token punctuation">;</span> <span class="token property">background-position</span><span class="token punctuation">:</span> 0 95%<span class="token punctuation">;</span> <span class="token property">-webkit-background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:hover, a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:visited</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #999<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* grey links */</span> <span class="token selector">.grey-links a</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #999<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.grey-links a:hover, .grey-links a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.grey-links a:active</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="pseudo-elements" style="position:relative;"><a href="#pseudo-elements" aria-label="pseudo elements permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pseudo-elements</h2> <p>I believe you are familiar with CSS triangles and have used them many times. Me too, and very often I use them to enrich the link styling, like this:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/keeping-css-short-with-currentcolor-3.jpg" data-alt="Link with CSS triangle"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 26.108374384236456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEG/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB0ECg/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABPyF//9oADAMBAAIAAwAAABBwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAAIDAAAAAAAAAAAAAAAAAAERABBB/9oACAEBAAE/EIAtdf/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/fea3473f17a13db933dc3d7ef3a4b49f/3466e/keeping-css-short-with-currentcolor-3.webp 203w, /static/fea3473f17a13db933dc3d7ef3a4b49f/d7d9a/keeping-css-short-with-currentcolor-3.webp 405w, /static/fea3473f17a13db933dc3d7ef3a4b49f/7df04/keeping-css-short-with-currentcolor-3.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/fea3473f17a13db933dc3d7ef3a4b49f/37414/keeping-css-short-with-currentcolor-3.jpg 203w, /static/fea3473f17a13db933dc3d7ef3a4b49f/32fb1/keeping-css-short-with-currentcolor-3.jpg 405w, /static/fea3473f17a13db933dc3d7ef3a4b49f/a6335/keeping-css-short-with-currentcolor-3.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/fea3473f17a13db933dc3d7ef3a4b49f/a6335/keeping-css-short-with-currentcolor-3.jpg" alt="Link with CSS triangle" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>CSS pseudo-element <code class="language-text">::after</code> is a triangle in this case. By using <code class="language-text">currentColor</code> you don’t have to repeat color settings for the triangle and its interaction states:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:hover, a:focus</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #333<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a:active</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #666<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a::after</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 0.5em solid transparent<span class="token punctuation">;</span> <span class="token property">border-right</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">a::after, a:hover::after, a:focus::after, a:active::after</span> <span class="token punctuation">{</span> <span class="token property">border-left-color</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="horizontal-line" style="position:relative;"><a href="#horizontal-line" aria-label="horizontal line permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Horizontal line</h2> <p>This isn’t an example of writing less code. It’s more like an example of writing efficient and maintainable code. The purpose of the horizontal line, the <code class="language-text">&lt;hr /></code>, is to divide different parts of the content. I think that visually the line shouldn’t be disturbing, it should be subtle:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/keeping-css-short-with-currentcolor-4.jpg" data-alt="Horizontal line"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 59.11330049261084%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd1UigD/xAAWEAEBAQAAAAAAAAAAAAAAAAAQEQD/2gAIAQEAAQUCZof/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAEBAQAAAAAAAAAAAAAAAAAxACD/2gAIAQEABj8CZz//xAAZEAADAAMAAAAAAAAAAAAAAAAAAREQITH/2gAIAQEAAT8hdo2Vwatk4//aAAwDAQACAAMAAAAQU8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhUTFhcf/aAAgBAQABPxDNoBq4bCcGb7lgVzPeABRP/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/48b1d4071d6eb3463d6d6c5291c651c3/3466e/keeping-css-short-with-currentcolor-4.webp 203w, /static/48b1d4071d6eb3463d6d6c5291c651c3/d7d9a/keeping-css-short-with-currentcolor-4.webp 405w, /static/48b1d4071d6eb3463d6d6c5291c651c3/7df04/keeping-css-short-with-currentcolor-4.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/48b1d4071d6eb3463d6d6c5291c651c3/37414/keeping-css-short-with-currentcolor-4.jpg 203w, /static/48b1d4071d6eb3463d6d6c5291c651c3/32fb1/keeping-css-short-with-currentcolor-4.jpg 405w, /static/48b1d4071d6eb3463d6d6c5291c651c3/a6335/keeping-css-short-with-currentcolor-4.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/48b1d4071d6eb3463d6d6c5291c651c3/a6335/keeping-css-short-with-currentcolor-4.jpg" alt="Horizontal line" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Dudley has already mentioned this among his examples, so I’m just repeating this and adding a little tweak:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.post</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.post hr</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 33%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 0.313em<span class="token punctuation">;</span> <span class="token comment">/* 5px */</span> <span class="token property">border</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span> <span class="token property">opacity</span><span class="token punctuation">:</span> .2<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>The smart thing about this code is that if you ever change the color of the text, you will not need to change the color of the horizontal line. Automation is what we always seek in our work. The more code we write, the more we value the automation solutions.</p> <h2 id="before-i-go" style="position:relative;"><a href="#before-i-go" aria-label="before i go permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Before I go…</h2> <p>Have any more practical and code-saving usages of <code class="language-text">currentColor</code>? Be sure to <a href="http://twitter.com/osvaldas" target="_blank" rel="nofollow noopener noreferrer">share</a> them.</p> <h2 id="more-reading" style="position:relative;"><a href="#more-reading" aria-label="more reading permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>More reading</h2> <ul> <li><a href="http://blogs.adobe.com/dreamweaver/2015/02/extending-the-color-cascade-with-the-css-currentcolor-variable.html#.VPh-20JVpvf" target="_blank" rel="nofollow noopener noreferrer">Extending the Color Cascade with the CSS currentColor Variable</a></li> </ul><![CDATA[Redesign]]>https://osvaldas.info/redesignhttps://osvaldas.info/redesignThu, 30 Oct 2014 00:00:00 GMT<p>After almost three years I have finally arrived at the point where I had to redesign my personal website. I was forced by a few reasons.</p> <p>The first one is that I had this huge image on the homepage for the newest blog post. It was okay as long as I had something unique to show, preferably my own creation. But what if I wanted to write a short post, a comment, for example, which has no relation to any unique graphics? Stock images – no, thank you. I wanted to lose this dependency.</p> <p>The second reason is the ad by AdPacks. I wanted to have it on every page of my website. Web design audience does not conflict with real, useful web design related ads. That’s one more reason to keep sharing web design techniques.</p> <p>Lastly, there were many small details which I was mad about and I knew I needed to eliminate these evils. Realignment just wouldn’t have worked out, so I put my the previous design under <a href="http://v2.osvaldas.info" target="_blank" rel="nofollow noopener noreferrer">v2.osvaldas.info</a> and came up with this…</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/redesign-1.jpg" data-alt="Redesign"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 71.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHbl1DAP//EABgQAAIDAAAAAAAAAAAAAAAAAAAQERIx/9oACAEBAAEFAsLOF//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABUQAQEAAAAAAAAAAAAAAAAAABAh/9oACAEBAAY/Amv/xAAaEAEAAQUAAAAAAAAAAAAAAAABEQAQITFR/9oACAEBAAE/IXZ61xFgcyknFANW/9oADAMBAAIAAwAAABBgD//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EGf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAQITFBwf/aAAgBAQABPxDhAvI4UnY+aezJQCQ+5biL7r//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/2dd11d2c0ca30ef5e6aad5306cdd7278/3466e/redesign-1.webp 203w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/d7d9a/redesign-1.webp 405w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/6da5f/redesign-1.webp 810w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/a6361/redesign-1.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/2dd11d2c0ca30ef5e6aad5306cdd7278/37414/redesign-1.jpg 203w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/32fb1/redesign-1.jpg 405w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/92b2a/redesign-1.jpg 810w, /static/2dd11d2c0ca30ef5e6aad5306cdd7278/fb816/redesign-1.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/2dd11d2c0ca30ef5e6aad5306cdd7278/92b2a/redesign-1.jpg" alt="Redesign" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="the-feel" style="position:relative;"><a href="#the-feel" aria-label="the feel permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The feel</h2> <p>I wanted a different mood for the website. A more personal and authentic feel, rather than white-color-dominating and sales-first design. I chose the background color similar to the color of my <a href="http://www.amazon.com/gp/product/8883701127/ref=as_li_tl?ie=UTF8&#x26;camp=1789&#x26;creative=390957&#x26;creativeASIN=8883701127&#x26;linkCode=as2&#x26;tag=osvaldas-20&#x26;linkId=NSG6J3WQTKWLPLKJ" target="_blank" rel="nofollow noopener noreferrer">Moleskine</a> notebook pages’ and a typace from Serif fonts family.</p> <p>Browsing through Google Fonts the typeface named <strong>Playfair Display</strong> quickly took my attention. It’s extremely good looking for large text but not so appealing for smaller sentences and paragraphs. For the later cases I am using Google’s famous Roboto. I’ve been struggling a lot to find a readable typeface and Roboto seemed to be a good way out.</p> <h2 id="layout" style="position:relative;"><a href="#layout" aria-label="layout permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Layout</h2> <p>Reading text is much easier when the content block is centered on the page because your face is naturally centered in relation to the screen. Despite the sidebar, the layout is centered in relation to the content block rather than the whole layout. Most of the time this helps to keep the content block centered. Try resizing the window to see how the layout behaves.</p> <p>On smaller screens the sidebar is transformed to a header-like element of the website, which auto hides when scrolling down in order to give more room for content.</p> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/110457870" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <h2 id="buttons" style="position:relative;"><a href="#buttons" aria-label="buttons permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Buttons</h2> <p>Quickly after Google introduced their new design language, front-enders created a button-click effect technique equivalent for web. It’s a nice effect, but it uses JavaScript and some extra markup which is why I think implementing is not reasonable. I wanted something similar but with a lighter implementation. Thank you, pseudo elements and CSS animation. I am going to share the code in one of my future blog posts. Inspired by Google’s Material Design and interpreted by me:</p> <p> <div class="video-embed"> <iframe title="" width="560" height="316" src="https://player.vimeo.com/video/110457869" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <h2 id="links" style="position:relative;"><a href="#links" aria-label="links permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Links</h2> <p>I was skeptical about this CSS gradients based technique when I first saw it, because it requires text shadow. Although, that is not a problem if you have a solid color background behind the links, which is exactly my case. Finally, <a href="http://dbushell.com" target="_blank" rel="nofollow noopener noreferrer">David</a>’s implementation persuaded me to try this out.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/redesign-2.jpg" data-alt="Redesign"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.70935960591133%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB2UsJGJD/xAAXEAEAAwAAAAAAAAAAAAAAAAAQAhFB/9oACAEBAAEFApO0f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAABERAxsf/aAAgBAQABPyHYTonUPkjH/9oADAMBAAIAAwAAABBcH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAMAAgMAAAAAAAAAAAAAAAABESFhEJGh/9oACAEBAAE/EK6EibxpmYkFThjX7x//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/23b82cc5231a0f66c2d1cea02f9323a2/3466e/redesign-2.webp 203w, /static/23b82cc5231a0f66c2d1cea02f9323a2/d7d9a/redesign-2.webp 405w, /static/23b82cc5231a0f66c2d1cea02f9323a2/7df04/redesign-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/23b82cc5231a0f66c2d1cea02f9323a2/37414/redesign-2.jpg 203w, /static/23b82cc5231a0f66c2d1cea02f9323a2/32fb1/redesign-2.jpg 405w, /static/23b82cc5231a0f66c2d1cea02f9323a2/a6335/redesign-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/23b82cc5231a0f66c2d1cea02f9323a2/a6335/redesign-2.jpg" alt="Redesign" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Beauty demands victims. The bad thing about this technique is that you have to define few properties: you have to know the background color behind the link, and you have to know the color of the link. I found a trick that basically eliminates that last flaw – it’s a CSS variable <code class="language-text">currentColor</code>. There are many more cases where it saves me lines of CSS and which I am going to document later.</p> <p>With the help of <a href="http://modernizr.com" target="_blank" rel="nofollow noopener noreferrer">Modernizr</a> I target browsers that do not support CSS gradients and browsers which have JavaScript disabled (because Modernizr will not function and most likely they do not support gradients as well) for fallback:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">html.no-js a, html.no-cssgradients a</span> <span class="token punctuation">{</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">html.cssgradients a</span> <span class="token punctuation">{</span> <span class="token property">text-shadow</span><span class="token punctuation">:</span> 2px 0 0 #f2f6ed<span class="token punctuation">,</span> -2px 0 0 #f2f6ed<span class="token punctuation">;</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span> to right<span class="token punctuation">,</span> currentColor 0%<span class="token punctuation">,</span> currentColor 100% <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat-x<span class="token punctuation">;</span> <span class="token property">background-position</span><span class="token punctuation">:</span> 0 95%<span class="token punctuation">;</span> <span class="token property">-webkit-background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token property">background-size</span><span class="token punctuation">:</span> 100% 1px<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="responsive-images" style="position:relative;"><a href="#responsive-images" aria-label="responsive images permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Responsive images</h2> <p>Native responsive images technique (<code class="language-text">[scrset]</code> and <code class="language-text">&lt;picture></code>) is making just the first steps – the browser compatibility is very low. Picturefill polyfil fails when JavaScript is disabled – no images will be displayed and crawled by search engines. I ended up writing my own polyfil. The technique is based on the usage of <code class="language-text">&lt;noscript></code> tag and has four main features:</p> <ol> <li>Images are displayed even if JavaScript is disabled;</li> <li>Images are reachable by search engines. Nick Power <a href="http://www.nickpower.com/google-index-links-noscript-tag/" target="_blank" rel="nofollow noopener noreferrer">did a test</a> to find out if the contents of <code class="language-text">&lt;noscript></code> are crawled by search engines. The result was positive;</li> <li>Images are <em>lazyloaded</em>. Images are loaded and displayed only if user will see them. Why download these megabytes if the user is not going to scroll down the page?</li> <li>Finally and most importantly, the script picks the most appropriate image source from <code class="language-text">data-srcset</code> attribute by using the the syntax of native <code class="language-text">[srcset]</code>.</li> </ol> <p>Let’s have a look to what this has helped to achieve so far. Take my <a href="/work">work</a> page for example. There are 31 large images. The “problem” is that a user may not necessarily scroll down the page and see all the images. What if they will just click the first link and use an in-page navigation to see the rest of the works? So, the initial size of the page viewing it on 13” laptop is <strong>1.34 mb</strong>. If a user scrolls down the page, the size increases up to <strong>3.50 mb</strong>. Repeating the same scenario on iPhone, the numbers are respectively <strong>0.78 mb</strong> and <strong>2.61 mb</strong>. Mobile data users would appreciate that.</p> <p>Green and resources-saving web design starts here. I am going to share the code very soon.</p> <h2 id="fonts" style="position:relative;"><a href="#fonts" aria-label="fonts permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Fonts</h2> <p>99% of the time I use Google Fonts. This is a third-party service which not necessarily works smoothly all the time. In order to prevent these services from badly influencing website loading, it’s a good practice to use third-party services in a non render blocking manner – asynchronously. Thanks to Filament Group for writing and sharing a JavaScript function <a href="https://github.com/filamentgroup/loadCSS" target="_blank" rel="nofollow noopener noreferrer">loadCSS</a>. Along with no-JavaScript fallback the insertion looks this:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>//fonts.googleapis.com/css?family=Roboto:400,400italic,700,700italic<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token keyword">function</span> <span class="token function">loadCSS</span><span class="token punctuation">(</span><span class="token parameter">e<span class="token punctuation">,</span>t<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token string">"use strict"</span><span class="token punctuation">;</span><span class="token keyword">var</span> r<span class="token operator">=</span>window<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"link"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> i<span class="token operator">=</span>t<span class="token operator">||</span>window<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">"script"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>r<span class="token punctuation">.</span>rel<span class="token operator">=</span><span class="token string">"stylesheet"</span><span class="token punctuation">;</span>r<span class="token punctuation">.</span>href<span class="token operator">=</span>e<span class="token punctuation">;</span>r<span class="token punctuation">.</span>media<span class="token operator">=</span><span class="token string">"only x"</span><span class="token punctuation">;</span>i<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">insertBefore</span><span class="token punctuation">(</span>r<span class="token punctuation">,</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>r<span class="token punctuation">.</span>media<span class="token operator">=</span>n<span class="token operator">||</span><span class="token string">"all"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">loadCSS</span><span class="token punctuation">(</span> <span class="token string">'//fonts.googleapis.com/css?family=Roboto:400,400italic,700,700italic'</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p>However, this does not prevent from <em>text flashing</em>. To fix that I am going to consider two techniques: <a href="https://gist.github.com/hdragomir/8f00ce2581795fd7b1b7" target="_blank" rel="nofollow noopener noreferrer">Smashing Magazine’s</a> and <a href="https://github.com/typekit/webfontloader" target="_blank" rel="nofollow noopener noreferrer">Web Font Loader</a>.</p> <h2 id="svg-icons" style="position:relative;"><a href="#svg-icons" aria-label="svg icons permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>SVG icons</h2> <p>The only rastered graphics on this website is images. I have fully switched to good ol’ SVG, because the device screens are improving so fast that <em>@2x</em> is not enough today. SVG saves the time and provides huge manipulation possibilities.</p> <p>Firstly, I tried encoding SVG files with <em>Base64</em> and inserting them directly into CSS. However, the method has various flaws and <a href="http://css-tricks.com/probably-dont-base64-svg/" target="_blank" rel="nofollow noopener noreferrer">isn’t recommended</a>. You cannot color the images by simply using CSS property <code class="language-text">fill</code>, neither take the advantage of <code class="language-text">viewbox</code> and other related attributes. On some browsers (i.e. IE Mobile) the images look blurry, etc. I am still using this method on Readerrr, but that will change soon.</p> <p>Finally, I stayed on <strong>Inline SVG</strong>. It is still weird for me to insert icons using HTML tags, because I have always treated icons as a <em>decoration</em>, not content. Anyway, I can ignore that because of the flexibility that Inline SVG gives.</p> <p>I have developed a custom, still experimental way of providing the SVG icons for the document. If possible it stores icons onto browser’s <code class="language-text">localStorage</code>. Else and if possible it loads icons asynchronously. It also guarantees that icons are displayed in both JavaScript and no-JavaScript environments, including IE 9, 10, 11. I am going share the code soon.</p> <h2 id="performance" style="position:relative;"><a href="#performance" aria-label="performance permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Performance</h2> <p>Besides asynchronous CSS, there is also a very similar way to load JavaScript files. Thanks again to Filament Group:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token keyword">function</span> <span class="token function">loadJS</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token string">"use strict"</span><span class="token punctuation">;</span><span class="token keyword">var</span> t<span class="token operator">=</span>window<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">"script"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">var</span> n<span class="token operator">=</span>window<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"script"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>n<span class="token punctuation">.</span>src<span class="token operator">=</span>e<span class="token punctuation">;</span>t<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">insertBefore</span><span class="token punctuation">(</span>n<span class="token punctuation">,</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> n<span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">'file.js'</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p>Putting this into <code class="language-text">&lt;head></code> means that downloading of the JavaScript file starts along with the downloading of the whole document. It also means that downloading of the document is not interrupted or blocked by downloading of the document. What a win.</p> <p>The fewer render-blocking cases the faster the website loads. Or, which is very important, at least gives that impression to a user.</p> <p>The size of the homepage is <strong>231 kb</strong>, including AdPacks ad. <strong>86 kb</strong> belongs to jQuery. I think the proportion is saying <em>“go jQuery-less”</em>…</p> <h2 id="newsletter" style="position:relative;"><a href="#newsletter" aria-label="newsletter permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Newsletter</h2> <p>I had this idea for some time now. I also had a newsletter module for my CodeIgniter based back-end system which I developed for my client. So I am double sizing the amount of options for subscribing to my news. No regular emails for now, just important news, such as big blog posts, shop items, or personal projects.</p><![CDATA[Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback]]>https://osvaldas.info/flexbox-based-responsive-equal-height-blocks-with-javascript-fallbackhttps://osvaldas.info/flexbox-based-responsive-equal-height-blocks-with-javascript-fallbackSat, 12 Jul 2014 00:00:00 GMT<p>After I’ve published the <a href="/responsive-equal-height-blocks">post</a> on how I had implemented responsive equal height blocks into <del>Readerrr</del>, I received some useful feedback from the community. <a href="https://twitter.com/dcsturm/status/487185077366185984" target="_blank" rel="nofollow noopener noreferrer">Daniel Sturm suggested</a> using CSS3’s Flexbox rather than JavaScript, and <a href="https://twitter.com/vpieters/status/487229179650658304" target="_blank" rel="nofollow noopener noreferrer">Veerle Pieters tweeted</a> <em>“&#x3C;...> you could do with Flexbox &#x26; use this as JS fallback”</em>. Exactly! How did I not think of that at all?! I’ve read several articles about Flexbox before but never tried it myself, therefore this thing totally went out of my head.</p> <p><strong>Why Flexbox?</strong> In short, the <em>Flexbox Layout</em> module was designed to solve problems exactly like this. It is an efficient and flexible way to manage probably all types of layouts. It provides almost no time gap between initially wrong and correctly laid out layout look. In JavaScript solution case it takes time to download the document, then to download the corresponding JS file, and, if there are any, download images in the blocks. Flexbox is instant and JavaScript takes seconds. Even so, this JavaScript case is perfect for people who use older browsers that do not support Flexbox.</p> <h2 id="the-problem" style="position:relative;"><a href="#the-problem" aria-label="the problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The problem</h2> <p>If you haven’t read my previous post, you don’t need to. Here’s the code and the problem (broken-like layout) to solve:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- content --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- content --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- other items --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.list</span> <span class="token punctuation">{</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token comment">/* just clearing floats */</span> <span class="token punctuation">}</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 25%<span class="token punctuation">;</span> <span class="token comment">/* 4 items per row */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.jpg" data-alt="Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 69.95073891625616%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgMA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGim0ejtP/EABoQAQADAAMAAAAAAAAAAAAAAAEAAgMREzH/2gAIAQEAAQUCMlTC5HO/Pk7LFXSf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BJ//EABwQAAEDBQAAAAAAAAAAAAAAAAABAjEQITJCkf/aAAgBAQAGPwJLtNSGUkyf0//EABoQAQADAQEBAAAAAAAAAAAAAAEAETEhkaH/2gAIAQEAAT8hPR60GCY0btWOs+jBcKSwL19nbmEf/9oADAMBAAIAAwAAABB/H//EABcRAAMBAAAAAAAAAAAAAAAAAAABEWH/2gAIAQMBAT8QcZcP/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERYf/aAAgBAgEBPxBI6iaf/8QAGxABAAMAAwEAAAAAAAAAAAAAAQARIUFRkTH/2gAIAQEAAT8QITbS08iPsNDvOTeGnEXrY7Go1iVbSvC8x646fRP/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/125f51143cb3f069076f748cd21ee5d5/3466e/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.webp 203w, /static/125f51143cb3f069076f748cd21ee5d5/d7d9a/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.webp 405w, /static/125f51143cb3f069076f748cd21ee5d5/7df04/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/125f51143cb3f069076f748cd21ee5d5/37414/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.jpg 203w, /static/125f51143cb3f069076f748cd21ee5d5/32fb1/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.jpg 405w, /static/125f51143cb3f069076f748cd21ee5d5/a6335/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/125f51143cb3f069076f748cd21ee5d5/a6335/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-1.jpg" alt="Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="the-solution" style="position:relative;"><a href="#the-solution" aria-label="the solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The solution</h2> <p>If you haven’t had any touch with Flexbox, you’ll be surprised how magical it is. <code class="language-text">display: flex</code> initiates Flexbox for the container element and <code class="language-text">flex-wrap: wrap</code> tells to wrap child items rather than fit them onto one line. Repeating <code class="language-text">display: flex</code> for the child items makes sure the elements have the same heights in their rows.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.list</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> -webkit-flex<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> -ms-flexbox<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">-webkit-flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span> <span class="token property">-ms-flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span> <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> -webkit-flex<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> -ms-flexbox<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.jpg" data-alt="Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 80.29556650246306%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDAf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAG46JpIX//EABoQAAIDAQEAAAAAAAAAAAAAAAIDAAEREhT/2gAIAQEAAQUC85ykHUJZ72cs2wmO3//EABURAQEAAAAAAAAAAAAAAAAAABAx/9oACAEDAQE/Aaf/xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAgEBPwGH/8QAHhAAAgEDBQAAAAAAAAAAAAAAAAIBESExEjJCYYL/2gAIAQEABj8CWuk4mEIu0+Rs9WNsn//EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQVGBcf/aAAgBAQABPyHYVIJzRu1Yqz4LA/aMJfZiac58T6n/2gAMAwEAAgADAAAAECfv/8QAFhEBAQEAAAAAAAAAAAAAAAAAERAx/9oACAEDAQE/EDRP/8QAGREAAQUAAAAAAAAAAAAAAAAAAAERITFh/9oACAECAQE/ELwo+n//xAAbEAEAAgMBAQAAAAAAAAAAAAABESEAMVFBof/aAAgBAQABPxApAWUFJ95iOt7Q37WWwEwvtDoDD5kA5lpmk87WP3SHnk8z/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/427dbab2bcf823b90f29e408bb3eeeb5/3466e/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.webp 203w, /static/427dbab2bcf823b90f29e408bb3eeeb5/d7d9a/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.webp 405w, /static/427dbab2bcf823b90f29e408bb3eeeb5/7df04/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/427dbab2bcf823b90f29e408bb3eeeb5/37414/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.jpg 203w, /static/427dbab2bcf823b90f29e408bb3eeeb5/32fb1/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.jpg 405w, /static/427dbab2bcf823b90f29e408bb3eeeb5/a6335/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/427dbab2bcf823b90f29e408bb3eeeb5/a6335/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback-2.jpg" alt="Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>This works perfectly in the latest versions of Chrome, Android, Safari, Opera, Firefox, and Internet Explorer 10+. For the rest I have a JavaScript pill.</p> <p>I did not include this into the previous CSS code, but some of the older WebKit browsers support the <em>old</em> Flexbox syntax (<code class="language-text">display: -webkit-box</code>). However, the wrapper property <code class="language-text">-webkit-box-lines: multiple</code> simply does not work on iOS Safari 6.1- nor on Android 4.3-.</p> <h2 id="javascript-fallback" style="position:relative;"><a href="#javascript-fallback" aria-label="javascript fallback permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript fallback</h2> <p>This part covers a fallback solution for such browsers as Internet Explorer 9-, Android 4.3-, iOS Safari 6.1-, and Opera Mini. I wrote a tiny piece of <strong>jQuery</strong>-dependent code which:</p> <ol> <li>Detects if the browser does not support Flexbox;</li> <li>Calculates the number of items per row by dividing the widths of <code class="language-text">.list</code> and <code class="language-text">.list__item</code>;</li> <li>Virtually divides the list into rows accordingly to that number;</li> <li>Detects which item has the biggest height in each row;</li> <li>Sets these heights for other items in each row correspondingly.</li> </ol> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> s <span class="token operator">=</span> document<span class="token punctuation">.</span>body <span class="token operator">||</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">,</span> s <span class="token operator">=</span> s<span class="token punctuation">.</span>style<span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>webkitFlexWrap <span class="token operator">==</span> <span class="token string">''</span> <span class="token operator">||</span> s<span class="token punctuation">.</span>msFlexWrap <span class="token operator">==</span> <span class="token string">''</span> <span class="token operator">||</span> s<span class="token punctuation">.</span>flexWrap <span class="token operator">==</span> <span class="token string">''</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">var</span> $list <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.list'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $items <span class="token operator">=</span> $list<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'.list__item'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function-variable function">setHeights</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $items<span class="token punctuation">.</span><span class="token function">css</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> <span class="token string">'auto'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> perRow <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>$list<span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> $items<span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>perRow <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> perRow <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> j <span class="token operator">=</span> $items<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i <span class="token operator">&lt;</span> j<span class="token punctuation">;</span> i <span class="token operator">+=</span> perRow<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> maxHeight <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> $row <span class="token operator">=</span> $items<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> i <span class="token operator">+</span> perRow<span class="token punctuation">)</span><span class="token punctuation">;</span> $row<span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> itemHeight <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span><span class="token function">$</span><span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">outerHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>itemHeight <span class="token operator">></span> maxHeight<span class="token punctuation">)</span> maxHeight <span class="token operator">=</span> itemHeight<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $row<span class="token punctuation">.</span><span class="token function">css</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> maxHeight<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">setHeights</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> setHeights<span class="token punctuation">)</span><span class="token punctuation">;</span> $list<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'img'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> setHeights<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>What if <strong>JavaScript is disabled</strong> in a browser? The problem is that CSS’s native feature detection is less supported than Flexbox itself. Therefore, using <code class="language-text">@support</code> rule will not cover every browser that supports Flexbox. But that’s better than nothing.</p> <p>What I suggest is to treat things this way: <em>disabled JavaScript = no Flexbox support</em> (I believe this equality is mostly correct) and fix the exceptions with the help of <code class="language-text">@support</code>. Technically, add a class name <code class="language-text">.no-js</code> to <code class="language-text">&lt;html></code> tag and remove it with JavaScript. That’s how we’ll know if it is disabled or not. Then, style the list items respectively and finally “compensate” this styling with the help of <code class="language-text">@supports</code>.</p> <p>I chose to present the blocks as full-width rows in this case. If there are any images, they will be aligned by the right edge of the row on larger screens.</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- your stuff --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e<span class="token punctuation">,</span>t<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> r<span class="token operator">=</span>e<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"html"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>r<span class="token punctuation">.</span>className<span class="token operator">=</span>r<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">(^|\s)no-js(\s|$)</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><span class="token string">"$1$2"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span>window<span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- remove this if you are using Modernizr --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- your stuff --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">html.no-js .list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">html.no-js .list__item img</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> 9.375rem<span class="token punctuation">;</span> <span class="token comment">/* 150 */</span> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 1.25rem<span class="token punctuation">;</span> <span class="token comment">/* 20 */</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">display</span><span class="token punctuation">:</span> -webkit-flex<span class="token punctuation">)</span> <span class="token keyword">or</span> <span class="token punctuation">(</span><span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> <span class="token selector">html.no-js .list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 25%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">html.no-js .list__item img</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-equal-height-blocks-5.jpg" data-alt="Responsive Equal Height Blocks"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.09852216748769%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGwysQTUJ//xAAZEAACAwEAAAAAAAAAAAAAAAABAgASMRH/2gAIAQEAAQUCGRu2GR0Fv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AUf/xAAYEAACAwAAAAAAAAAAAAAAAAAAQQEQkf/aAAgBAQAGPwJCwm2f/8QAGxAAAgMAAwAAAAAAAAAAAAAAAAERIVFhgZH/2gAIAQEAAT8hhDjhRP2GM4WJvWTN+j//2gAMAwEAAgADAAAAELM//8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EG1//8QAFhEBAQEAAAAAAAAAAAAAAAAAABFR/9oACAECAQE/EE6//8QAHBABAAIDAQEBAAAAAAAAAAAAAQARITFB0VGx/9oACAEBAAE/EObq7+Tf0hiLTgVWWBoWj8jXyhstaubdn//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/15b4a48a13def8e185eb6f11d8046960/3466e/responsive-equal-height-blocks-5.webp 203w, /static/15b4a48a13def8e185eb6f11d8046960/d7d9a/responsive-equal-height-blocks-5.webp 405w, /static/15b4a48a13def8e185eb6f11d8046960/7df04/responsive-equal-height-blocks-5.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/15b4a48a13def8e185eb6f11d8046960/37414/responsive-equal-height-blocks-5.jpg 203w, /static/15b4a48a13def8e185eb6f11d8046960/32fb1/responsive-equal-height-blocks-5.jpg 405w, /static/15b4a48a13def8e185eb6f11d8046960/a6335/responsive-equal-height-blocks-5.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/15b4a48a13def8e185eb6f11d8046960/a6335/responsive-equal-height-blocks-5.jpg" alt="Responsive Equal Height Blocks" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p><a href="https://v3.osvaldas.info/examples/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p><![CDATA[Responsive Equal Height Blocks]]>https://osvaldas.info/responsive-equal-height-blockshttps://osvaldas.info/responsive-equal-height-blocksThu, 10 Jul 2014 00:00:00 GMT<p><strong>Update:</strong> after publishing this post I received some useful feedback from the community and wrote a new post with much smarter solution to the problem. I recommend you reading <strong><a href="/flexbox-based-responsive-equal-height-blocks-with-javascript-fallback">Flexbox Based Responsive Equal Height Blocks With JavaScript Fallback</a></strong> instead._</p> <p>When I was designing <del>Readerrr’s</del> features page, I wanted to put the features list content in a semantic markup, i.e. <code class="language-text">&lt;ul></code>, <code class="language-text">&lt;li></code>, and to have two list items per row on larger screens as well as one item per row on narrower screens. Each item has a different unknown amount of content which means every list item has a different height. Therefore, when you float them next to each other, you naturally get the layout which looks kinda broken, like this:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-equal-height-blocks-1.jpg" data-alt="Responsive Equal Height Blocks"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 100.98522167487684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAEEAgMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAbO+ZTIHMshZB//EABkQAAMBAQEAAAAAAAAAAAAAAAABAgMQMf/aAAgBAQABBQKdBaSyhzmkphquRo0V7//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABsQAAICAwEAAAAAAAAAAAAAAAABECECETFB/9oACAEBAAY/Ars5ZwtG0j2FjH//xAAdEAEAAgICAwAAAAAAAAAAAAABACERMRBxQYGR/9oACAEBAAE/IVGoOiUdF4xBesBoT1Ds2K+uCADGY3Dqf//aAAwDAQACAAMAAAAQTPiD/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxAf/8QAIBAAAgIABgMAAAAAAAAAAAAAAREAIRAxQVFhcZGx8P/aAAgBAQABPxAPSQgAA+VB2YULLe8AqyON4LdBp2dOImIgFgBGvc3xXsecGVNGi7PczR8Jn//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/e9c9d0ae4f7731c66d3994be1dcc60e9/3466e/responsive-equal-height-blocks-1.webp 203w, /static/e9c9d0ae4f7731c66d3994be1dcc60e9/d7d9a/responsive-equal-height-blocks-1.webp 405w, /static/e9c9d0ae4f7731c66d3994be1dcc60e9/7df04/responsive-equal-height-blocks-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/e9c9d0ae4f7731c66d3994be1dcc60e9/37414/responsive-equal-height-blocks-1.jpg 203w, /static/e9c9d0ae4f7731c66d3994be1dcc60e9/32fb1/responsive-equal-height-blocks-1.jpg 405w, /static/e9c9d0ae4f7731c66d3994be1dcc60e9/a6335/responsive-equal-height-blocks-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/e9c9d0ae4f7731c66d3994be1dcc60e9/a6335/responsive-equal-height-blocks-1.jpg" alt="Responsive Equal Height Blocks" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>This could be fixed by virtually dividing the list into rows, picking the height of the highest item in each row, and setting it for the other corresponding row items. Most importantly, this should be done in a <em><strong>responsive</strong></em> way:</p> <p><img src="/79341951d9611d1627c6fdcf024f3b89/responsive-equal-height-blocks-2.gif" alt="Responsive Equal Height Blocks"></p> <h2 id="basic-layout" style="position:relative;"><a href="#basic-layout" aria-label="basic layout permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Basic layout</h2> <p>Let’s start from where we began by constructing the “problematic” layout:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- content --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- content --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- other items --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span></code></pre></div> <p>In my example I’ll initially have 4 items (100% ÷ 4 = 25%) per row:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.list</span> <span class="token punctuation">{</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token comment">/* just clearing floats */</span> <span class="token punctuation">}</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 25%<span class="token punctuation">;</span> <span class="token comment">/* 4 items per row */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>As I mentioned and showed previously, this code produces the layout which looks broken.</p> <h2 id="responsive-equal-heights" style="position:relative;"><a href="#responsive-equal-heights" aria-label="responsive equal heights permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Responsive equal heights</h2> <p>Before controlling the heights, let’s set the number of blocks per row for smaller screens with the help of CSS media queries:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css">@media screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 50em<span class="token punctuation">)</span> <span class="token comment">/* 800px */</span> <span class="token punctuation">{</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 33.333%<span class="token punctuation">;</span> <span class="token comment">/* 3 items per row */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> @media screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 40em<span class="token punctuation">)</span> <span class="token comment">/* 640px */</span> <span class="token punctuation">{</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token comment">/* 2 items per row */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> @media screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 20em<span class="token punctuation">)</span> <span class="token comment">/* 320px */</span> <span class="token punctuation">{</span> <span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token comment">/* 1 item per row */</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>I wrote a tiny piece of jQuery-dependent code which:</p> <ol> <li>Calculates the number of items per row by dividing the widths of <code class="language-text">.list</code> and <code class="language-text">.list__item</code>;</li> <li>Virtually divides the list into rows accordingly to that number;</li> <li>Detects which item has the biggest height in each row;</li> <li>Sets these heights for other items in each row correspondingly.</li> </ol> <p>These steps are performed once when the page loads and repeated every time the browser’s window resizes.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">'use strict'</span><span class="token punctuation">;</span> <span class="token keyword">var</span> $list <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.list'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $items <span class="token operator">=</span> $list<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'.list__item'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function-variable function">setHeights</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $items<span class="token punctuation">.</span><span class="token function">css</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> <span class="token string">'auto'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> perRow <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>$list<span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> $items<span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>perRow <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> perRow <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> j <span class="token operator">=</span> $items<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i <span class="token operator">&lt;</span> j<span class="token punctuation">;</span> i <span class="token operator">+=</span> perRow<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> maxHeight <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> $row <span class="token operator">=</span> $items<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> i <span class="token operator">+</span> perRow<span class="token punctuation">)</span><span class="token punctuation">;</span> $row<span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> itemHeight <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">outerHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>itemHeight <span class="token operator">></span> maxHeight<span class="token punctuation">)</span> maxHeight <span class="token operator">=</span> itemHeight<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $row<span class="token punctuation">.</span><span class="token function">css</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> maxHeight<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">setHeights</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> setHeights<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span> jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="what-if-there-are-images-in-the-blocks" style="position:relative;"><a href="#what-if-there-are-images-in-the-blocks" aria-label="what if there are images in the blocks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What if there are images in the blocks?</h2> <p>This part is not relevant if there are widths and heights defined for images in HTML or CSS. However, here’s what will happen if the exact <strong>dimensions of images are unknown</strong>:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-equal-height-blocks-3.jpg" data-alt="Responsive Equal Height Blocks"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 39.90147783251231%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAULNg//EABkQAAIDAQAAAAAAAAAAAAAAAAECABEhMf/aAAgBAQABBQILbDIe/wD/xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAwEBPwGn/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAS/9oACAECAQE/AUyz/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECESExQv/aAAgBAQAGPwJZRuNo5P/EABoQAAIDAQEAAAAAAAAAAAAAAAEhABEx0ZH/2gAIAQEAAT8h2usF8ghbSMvkdvFz/9oADAMBAAIAAwAAABCL7//EABcRAQEBAQAAAAAAAAAAAAAAAAEAETH/2gAIAQMBAT8QHptv/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EKEGj//EABsQAQACAgMAAAAAAAAAAAAAAAEAMRFBIXHh/9oACAEBAAE/EMcAHMTqyBCy5fuEq4XfqT//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/4f18069f9ca7ca11a59c83df5fdbb0e8/3466e/responsive-equal-height-blocks-3.webp 203w, /static/4f18069f9ca7ca11a59c83df5fdbb0e8/d7d9a/responsive-equal-height-blocks-3.webp 405w, /static/4f18069f9ca7ca11a59c83df5fdbb0e8/7df04/responsive-equal-height-blocks-3.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/4f18069f9ca7ca11a59c83df5fdbb0e8/37414/responsive-equal-height-blocks-3.jpg 203w, /static/4f18069f9ca7ca11a59c83df5fdbb0e8/32fb1/responsive-equal-height-blocks-3.jpg 405w, /static/4f18069f9ca7ca11a59c83df5fdbb0e8/a6335/responsive-equal-height-blocks-3.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/4f18069f9ca7ca11a59c83df5fdbb0e8/a6335/responsive-equal-height-blocks-3.jpg" alt="Responsive Equal Height Blocks" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>The way out is to repeat these four mighty steps by calling the respective function within each image load. This serves fine if you have just a few blocks and lightweight images.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// ...</span> <span class="token function">setHeights</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> setHeights<span class="token punctuation">)</span><span class="token punctuation">;</span> $list<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'img'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> setHeights<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// the trick!</span></code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-equal-height-blocks-4.jpg" data-alt="Responsive Equal Height Blocks"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 61.08374384236453%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAbtxsowP/8QAGhABAAIDAQAAAAAAAAAAAAAAAgEDABESFP/aAAgBAQABBQLzvIocYq3sNzKtfSse/wD/xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAwEBPwGn/8QAFhEAAwAAAAAAAAAAAAAAAAAAEBES/9oACAECAQE/AVI//8QAGhAAAgMBAQAAAAAAAAAAAAAAAAIiMUIREv/aAAgBAQAGPwJe+TJgWQ8qLP/EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQWGh4f/aAAgBAQABPyHYVIJzRu24qyeoZLp6IEtFjBPkZ//aAAwDAQACAAMAAAAQk/8A/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxCrRT//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQIBAT8QUrG//8QAGxABAAMBAQEBAAAAAAAAAAAAAQARIUFR0eH/2gAIAQEAAT8QCyVtCy++RVTU2x3uTWKnISVBz8ITFSp6F+QVbuHHyf/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/08b56579a3711e0618bc9b4c8288a53c/3466e/responsive-equal-height-blocks-4.webp 203w, /static/08b56579a3711e0618bc9b4c8288a53c/d7d9a/responsive-equal-height-blocks-4.webp 405w, /static/08b56579a3711e0618bc9b4c8288a53c/7df04/responsive-equal-height-blocks-4.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/08b56579a3711e0618bc9b4c8288a53c/37414/responsive-equal-height-blocks-4.jpg 203w, /static/08b56579a3711e0618bc9b4c8288a53c/32fb1/responsive-equal-height-blocks-4.jpg 405w, /static/08b56579a3711e0618bc9b4c8288a53c/a6335/responsive-equal-height-blocks-4.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/08b56579a3711e0618bc9b4c8288a53c/a6335/responsive-equal-height-blocks-4.jpg" alt="Responsive Equal Height Blocks" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>However, if you are about to build a <strong>huge gallery</strong> or some kind of <strong>masonry</strong> with a lot of heavyweight images, this would not work well because of the constant visual content overlaps and “jumps” while images are being downloaded. I suggest loading the images manually and building the layout block by block.</p> <p>To start with, let’s put each image tag into <code class="language-text">&lt;noscript>&lt;/noscript></code>. These images will not be loaded by the browser if JavaScript is enabled. Luckily, they will remain accessible for search engines and will be loaded if JavaScript is disabled!</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>figure</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item__image<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>figcaption</span><span class="token punctuation">></span></span>Text<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>figcaption</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>figure</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></code></pre></div> <p>Now hide the blocks with some CSS and, if you prefer, define the <code class="language-text">transition</code> for <code class="language-text">opacity</code> for animated block fade-in:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.list__item</span> <span class="token punctuation">{</span> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">-webkit-transition</span><span class="token punctuation">:</span> opacity .5s linear<span class="token punctuation">;</span> <span class="token property">transition</span><span class="token punctuation">:</span> opacity .5s linear<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>One more tiny piece of jQuery-dependent code recursively replaces all the <code class="language-text">noscript</code>’s with their child <code class="language-text">img</code>’s, loads them, and fades in their parent blocks one after another.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">loadImages</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $items<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token string">'.js-load-images:first'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $<span class="token keyword">this</span> <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> $imgs <span class="token operator">=</span> $<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'noscript.list__item__image'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> imgTotal <span class="token operator">=</span> $imgs<span class="token punctuation">.</span>length<span class="token punctuation">,</span> imgLoaded <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> $imgs<span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $noscript <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $img <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>$noscript<span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $img<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $noscript<span class="token punctuation">.</span><span class="token function">replaceWith</span><span class="token punctuation">(</span>$img<span class="token punctuation">)</span><span class="token punctuation">;</span> imgLoaded<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>imgLoaded <span class="token operator">>=</span> imgTotal<span class="token punctuation">)</span> <span class="token punctuation">{</span> $<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">css</span><span class="token punctuation">(</span><span class="token string">'opacity'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setHeights</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">loadImages</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> $items<span class="token punctuation">.</span><span class="token function">addClass</span><span class="token punctuation">(</span><span class="token string">'js-load-images'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">loadImages</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="what-if-javascript-is-disabled" style="position:relative;"><a href="#what-if-javascript-is-disabled" aria-label="what if javascript is disabled permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What if JavaScript is disabled?</h2> <p><em>Graceful degradation</em> using CSS! In order to check whether JavaScript is enabled or disabled via CSS, append a class name <code class="language-text">no-js</code> to <code class="language-text">html</code> tag and remove it using JavaScript (you can skip the script part if you are using Modernizr in the document). Naturally, the class name will not be removed if JavaScript is disabled.</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- your stuff --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e<span class="token punctuation">,</span>t<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> r<span class="token operator">=</span>e<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"html"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>r<span class="token punctuation">.</span>className<span class="token operator">=</span>r<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">(^|\s)no-js(\s|$)</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><span class="token string">"$1$2"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span>window<span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- your stuff --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre></div> <p>Now you can easily modify the blocks for no-JavaScript case. I chose to present the blocks as full-width rows. If there are any images, they will be aligned by the right edge of the row on larger screens:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">html.no-js .list__item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">html.no-js .list__item img</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> 9.375em<span class="token punctuation">;</span> <span class="token comment">/* 150px */</span> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 1.25em<span class="token punctuation">;</span> <span class="token comment">/* 20px */</span> <span class="token punctuation">}</span> @media screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 20em<span class="token punctuation">)</span> <span class="token comment">/* 320px */</span> <span class="token punctuation">{</span> <span class="token selector">html.no-js .list__item img</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-equal-height-blocks-5.jpg" data-alt="Responsive Equal Height Blocks"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.09852216748769%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGwysQTUJ//xAAZEAACAwEAAAAAAAAAAAAAAAABAgASMRH/2gAIAQEAAQUCGRu2GR0Fv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AUf/xAAYEAACAwAAAAAAAAAAAAAAAAAAQQEQkf/aAAgBAQAGPwJCwm2f/8QAGxAAAgMAAwAAAAAAAAAAAAAAAAERIVFhgZH/2gAIAQEAAT8hhDjhRP2GM4WJvWTN+j//2gAMAwEAAgADAAAAELM//8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EG1//8QAFhEBAQEAAAAAAAAAAAAAAAAAABFR/9oACAECAQE/EE6//8QAHBABAAIDAQEBAAAAAAAAAAAAAQARITFB0VGx/9oACAEBAAE/EObq7+Tf0hiLTgVWWBoWj8jXyhstaubdn//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/15b4a48a13def8e185eb6f11d8046960/3466e/responsive-equal-height-blocks-5.webp 203w, /static/15b4a48a13def8e185eb6f11d8046960/d7d9a/responsive-equal-height-blocks-5.webp 405w, /static/15b4a48a13def8e185eb6f11d8046960/7df04/responsive-equal-height-blocks-5.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/15b4a48a13def8e185eb6f11d8046960/37414/responsive-equal-height-blocks-5.jpg 203w, /static/15b4a48a13def8e185eb6f11d8046960/32fb1/responsive-equal-height-blocks-5.jpg 405w, /static/15b4a48a13def8e185eb6f11d8046960/a6335/responsive-equal-height-blocks-5.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/15b4a48a13def8e185eb6f11d8046960/a6335/responsive-equal-height-blocks-5.jpg" alt="Responsive Equal Height Blocks" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p>Three types of examples for you to try and investigate:</p> <p><a href="https://v3.osvaldas.info/examples/responsive-equal-height-blocks" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p><![CDATA[Minimalist Newsletter Subscription Form]]>https://osvaldas.info/minimalist-newsletter-subscription-formhttps://osvaldas.info/minimalist-newsletter-subscription-formThu, 12 Jun 2014 00:00:00 GMT<p>Subtle, minimalist and intuitive UI is what I like most about web design, having the visual part of it in mind. The design is in details. Refine each of them in the context of user experience and you’re almost perfect.</p> <p>Newsletter subscription form is the detail that can easily get you one step closer to the perfection. But it’s probably the most simple web form with just an input field and a submit button. "What more can you do with it?" you may ask. The first impression can be deceptive: there are quite a few things you can do with it, such as floating label, unusual visual approach, etc. But there’s one thing we’ll surely do now: hide the submit button by default and only show it if user enters the correct email address.</p> <p><img src="/9c139bed661ce146d70aafeae55685be/minimalist-newsletter-subscription-form-1.gif" alt="Minimalist Newsletter Subscription Form"></p> <h2 id="html" style="position:relative;"><a href="#html" aria-label="html permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML</h2> <p>Traditionally, let’s start with the minimalist markup for minimalist form:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>newsletter<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Enter your email address<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>OK<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="javascript" style="position:relative;"><a href="#javascript" aria-label="javascript permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript</h2> <p>This sets a “trap” on the input field to check with a little help from <abbr title="Regular Expressions">RegExp</abbr> if the typed text has “symptoms” of an actual email address. The check is performed with each symbol typed. In case of success a custom class name is added, else – removed. Just that. The rest will be handled using CSS.</p> <h3 id="with-jquery-dependency" style="position:relative;"><a href="#with-jquery-dependency" aria-label="with jquery dependency permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>With jQuery dependency</h3> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">'use strict'</span><span class="token punctuation">;</span> <span class="token keyword">var</span> form <span class="token operator">=</span> <span class="token string">'.newsletter'</span><span class="token punctuation">,</span> className <span class="token operator">=</span> <span class="token string">'newsletter--active'</span><span class="token punctuation">,</span> email <span class="token operator">=</span> <span class="token string">'input[type="email"]'</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span>form<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $form <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">,</span> $email <span class="token operator">=</span> $form<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>email<span class="token punctuation">)</span><span class="token punctuation">,</span> val <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span> $email<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'keyup.addClassWhenEmail'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> val <span class="token operator">=</span> $email<span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $form<span class="token punctuation">.</span><span class="token function">toggleClass</span><span class="token punctuation">(</span>className<span class="token punctuation">,</span> val <span class="token operator">!=</span> <span class="token string">''</span> <span class="token operator">&amp;&amp;</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^([\w-\.]+@([\w-]+\.)+[\w-]{2,12})?$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The code is compatible with both 1.x (IE6+) and 2.x (IE9+) library branches. The namespace <code class="language-text">addClassWhenEmail</code> for <code class="language-text">keyup</code> event provides a possibility to remove the functionality whenever you need this, for example:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.newsletter input[type="email"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">off</span><span class="token punctuation">(</span><span class="token string">'.addClassWhenEmail'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="without-dependencies-javascript-code" style="position:relative;"><a href="#without-dependencies-javascript-code" aria-label="without dependencies javascript code permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Without dependencies JavaScript code</h3> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">'use strict'</span><span class="token punctuation">;</span> <span class="token keyword">var</span> form <span class="token operator">=</span> <span class="token string">'.newsletter'</span><span class="token punctuation">,</span> className <span class="token operator">=</span> <span class="token string">'newsletter--active'</span><span class="token punctuation">,</span> email <span class="token operator">=</span> <span class="token string">'input[type="email"]'</span><span class="token punctuation">,</span> <span class="token function-variable function">addEventListener</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">element<span class="token punctuation">,</span> event<span class="token punctuation">,</span> handler</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> element<span class="token punctuation">.</span>addEventListener <span class="token operator">?</span> element<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span>event<span class="token punctuation">,</span> handler<span class="token punctuation">)</span> <span class="token operator">:</span> element<span class="token punctuation">.</span><span class="token function">attachEvent</span><span class="token punctuation">(</span><span class="token string">'on'</span> <span class="token operator">+</span> event<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">handler</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">forEach</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">elements<span class="token punctuation">,</span> fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> elements<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token function">fn</span><span class="token punctuation">(</span>elements<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">addClass</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">element<span class="token punctuation">,</span> className</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> element<span class="token punctuation">.</span>classList <span class="token operator">?</span> element<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span> <span class="token operator">:</span> element<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">' '</span> <span class="token operator">+</span> className<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">removeClass</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">element<span class="token punctuation">,</span> className</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> element<span class="token punctuation">.</span>classList <span class="token operator">?</span> element<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span> <span class="token operator">:</span> element<span class="token punctuation">.</span>className <span class="token operator">+=</span> element<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span> <span class="token string">'(^|\\b)'</span> <span class="token operator">+</span> className<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">' '</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'|'</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'(\\b|$)'</span><span class="token punctuation">,</span> <span class="token string">'gi'</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">' '</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">forEach</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span>form<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$form</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $email <span class="token operator">=</span> $form<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span>email<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>$email<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span> $email <span class="token operator">=</span> $email<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token function">addEventListener</span><span class="token punctuation">(</span>$email<span class="token punctuation">,</span> <span class="token string">'keyup'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $email<span class="token punctuation">.</span>value <span class="token operator">!=</span> <span class="token string">''</span> <span class="token operator">&amp;&amp;</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^([\w-\.]+@([\w-]+\.)+[\w-]{2,12})?$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span>$email<span class="token punctuation">.</span>value<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token function">addClass</span><span class="token punctuation">(</span>$form<span class="token punctuation">,</span> className<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">removeClass</span><span class="token punctuation">(</span>$form<span class="token punctuation">,</span> className<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span> document<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The code functions in Internet Explorer 8+. All fine with the other browsers.</p> <p>If you paid attention, you saw I was using a loop. This means you can have multiple newsletter subscription forms on the page and each of them will be served. If you need different configurations for each form, check the end of the post for plugin version.</p> <h3 id="configuration" style="position:relative;"><a href="#configuration" aria-label="configuration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Configuration</h3> <p>There are three variable values that you might want to change accordingly to your selectors eco-system:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> form <span class="token operator">=</span> <span class="token string">'.newsletter'</span><span class="token punctuation">,</span> <span class="token comment">// form selector</span> className <span class="token operator">=</span> <span class="token string">'newsletter--active'</span><span class="token punctuation">,</span> <span class="token comment">// class name for form when correct email is entered</span> email <span class="token operator">=</span> <span class="token string">'input[type="email"]'</span><span class="token punctuation">,</span> <span class="token comment">// email input field selector</span></code></pre></div> <h2 id="css" style="position:relative;"><a href="#css" aria-label="css permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS</h2> <p>The JavaScript part provides us with a full control of form management when there is a correct email address entered. In my case the button hidden or displayed.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.newsletter:not(.newsletter--active) input[type='submit']</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>I’m also using a simple CSS animation to display the button in style. Check it!</p> <p><a href="https://v3.osvaldas.info/examples/minimalist-newsletter-subscription-form" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <h2 id="plugin-version" style="position:relative;"><a href="#plugin-version" aria-label="plugin version permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Plugin version</h2> <p>For your convenience, I wrapped the JavaScript code into a form of a plugin, so that you can have multiple instances with different options if you wish. The same settings and browser compatibility applies.</p> <ul> <li><a href="https://v3.osvaldas.info/examples/minimalist-newsletter-subscription-form/jquery.add-class-when-email.js" target="_blank" rel="nofollow noopener noreferrer">jquery.add-class-when-email.js</a>; 366 bytes when minified.</li> <li><a href="https://v3.osvaldas.info/examples/minimalist-newsletter-subscription-form/add-class-when-email.js" target="_blank" rel="nofollow noopener noreferrer">add-class-when-email.js</a>; 867 bytes when minified.</li> </ul> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// jQuery</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.newsletter'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addClassWhenEmail</span><span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">'newsletter--active'</span><span class="token punctuation">,</span> <span class="token literal-property property">email</span><span class="token operator">:</span> <span class="token string">'input[type="email"]'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// non-jQuery</span> <span class="token function">addClassWhenEmail</span><span class="token punctuation">(</span><span class="token string">'.newsletter'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">'newsletter--active'</span><span class="token punctuation">,</span> <span class="token literal-property property">email</span><span class="token operator">:</span> <span class="token string">'input[type="email"]'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="a-thought" style="position:relative;"><a href="#a-thought" aria-label="a thought permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>A thought</h2> <p>It’s not an <em>absolute truth</em> that you should always hide the button by default. The decision should be dictated by the situation: importance and context.</p> <h2 id="update" style="position:relative;"><a href="#update" aria-label="update permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Update</h2> <p>After publishing this post, I submitted it to Designer News and received some well-aimed feedback. Dan Klammer observed that we can control the button without JavaScript by using CSS’s pseudo-class <code class="language-text">:valid</code>. However it’s quite poorly supported today. IE supports it from the version 10, Android from 4.4.3, not supported in Mobile Safari at all. Also, this method does not give control for the whole form, only for the button.</p><![CDATA[Keyboard Shortcuts for Pagination]]>https://osvaldas.info/keyboard-shortcuts-for-paginationhttps://osvaldas.info/keyboard-shortcuts-for-paginationTue, 03 Jun 2014 00:00:00 GMT<p>Computers have multiple input devices for easier usage. Imagine entering text without a keyboard or using a website without a mouse. Therefore we, people who make websites, should enable these devices on our products. The most basic but very useful thing would be keyboard shortcuts for pagination.</p> <p>When I was developing a custom Tumblr theme for <del>Tree House Love</del>, I wanted to make navigating through the posts as easy as possible. I started by designing visually obvious Previous / Next buttons and ended up by applying a custom keyboard event callback via JavaScript. The latter is super-easy and should be “implanted” anywhere where this could be usable on the web. For the sake of better UX.</p> <h2 id="how-it-works" style="position:relative;"><a href="#how-it-works" aria-label="how it works permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>How it works</h2> <p>The custom callback function code detects which one of the two keyboard arrow keys were pressed: left or right. It then selects a corresponding pagination link and simply simulates (triggers) a click event for it.</p> <h2 id="pagination-patterns" style="position:relative;"><a href="#pagination-patterns" aria-label="pagination patterns permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pagination patterns</h2> <p>In UI world there are a few patterns of pagination. The pattern could be a single or a combination of the following options:</p> <ul> <li>Numbered (1, 2, 3, …) links;</li> <li>Previous page and Next page links;</li> <li>First page and Last page links.</li> </ul> <p>Usually, the 3rd option is only used in a combination with other options.</p> <h2 id="html" style="position:relative;"><a href="#html" aria-label="html permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML</h2> <p>Let’s “illustrate” the most common pagination patterns with some accessible markup.</p> <h3 id="numbered-1-2-3--links" style="position:relative;"><a href="#numbered-1-2-3--links" aria-label="numbered 1 2 3 links permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Numbered (1, 2, 3, …) links</h3> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/keyboard-shortcuts-for-pagination-1.jpg?align=overflow" data-alt="Numbered (1, 2, 3, &#x2026;) links" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.586206896551722%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAED/9oADAMBAAIQAxAAAAHfS6gf/8QAFxABAQEBAAAAAAAAAAAAAAAAAwATAf/aAAgBAQABBQLqFalalf/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAMqH/2gAIAQEABj8CWCQSH//EABkQAAIDAQAAAAAAAAAAAAAAAAABESHwgf/aAAgBAQABPyFpVHkwUzQ//9oADAMBAAIAAwAAABCH3//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIQ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhAAAgIDAAAAAAAAAAAAAAAAAWEA8BFRwf/aAAgBAQABPxAoBz3l7GquOXc//9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/5acd82c412f411dc5f2377f21df4c6cf/3466e/keyboard-shortcuts-for-pagination-1.webp 203w, /static/5acd82c412f411dc5f2377f21df4c6cf/d7d9a/keyboard-shortcuts-for-pagination-1.webp 405w, /static/5acd82c412f411dc5f2377f21df4c6cf/6da5f/keyboard-shortcuts-for-pagination-1.webp 810w, /static/5acd82c412f411dc5f2377f21df4c6cf/a6361/keyboard-shortcuts-for-pagination-1.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/5acd82c412f411dc5f2377f21df4c6cf/37414/keyboard-shortcuts-for-pagination-1.jpg 203w, /static/5acd82c412f411dc5f2377f21df4c6cf/32fb1/keyboard-shortcuts-for-pagination-1.jpg 405w, /static/5acd82c412f411dc5f2377f21df4c6cf/92b2a/keyboard-shortcuts-for-pagination-1.jpg 810w, /static/5acd82c412f411dc5f2377f21df4c6cf/fb816/keyboard-shortcuts-for-pagination-1.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/5acd82c412f411dc5f2377f21df4c6cf/92b2a/keyboard-shortcuts-for-pagination-1.jpg" alt="Numbered (1, 2, 3, &#x2026;) links" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>navigation<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pagination<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number pagination-current<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 1, current page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 1, current page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 2<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 3<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 4<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 5<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>5<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span></code></pre></div> <h3 id="previous-and-next-links" style="position:relative;"><a href="#previous-and-next-links" aria-label="previous and next links permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Previous and Next links</h3> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/keyboard-shortcuts-for-pagination-2.jpg?align=overflow" data-alt="Previous and Next links" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.586206896551722%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQP/2gAMAwEAAhADEAAAAaAuoH//xAAWEAADAAAAAAAAAAAAAAAAAAAAAxP/2gAIAQEAAQUCqoqoqo//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwGI/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAADKh/9oACAEBAAY/AkgkEh//xAAWEAADAAAAAAAAAAAAAAAAAAAAAfD/2gAIAQEAAT8hmhBSaH//2gAMAwEAAgADAAAAEPff/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8QhD//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAADAQEBAAAAAAAAAAAAAAAAAWHwQVH/2gAIAQEAAT8Qs3Rd3efSzdP/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/06cd3247f1bd70f2d20d3768af15601f/3466e/keyboard-shortcuts-for-pagination-2.webp 203w, /static/06cd3247f1bd70f2d20d3768af15601f/d7d9a/keyboard-shortcuts-for-pagination-2.webp 405w, /static/06cd3247f1bd70f2d20d3768af15601f/6da5f/keyboard-shortcuts-for-pagination-2.webp 810w, /static/06cd3247f1bd70f2d20d3768af15601f/a6361/keyboard-shortcuts-for-pagination-2.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/06cd3247f1bd70f2d20d3768af15601f/37414/keyboard-shortcuts-for-pagination-2.jpg 203w, /static/06cd3247f1bd70f2d20d3768af15601f/32fb1/keyboard-shortcuts-for-pagination-2.jpg 405w, /static/06cd3247f1bd70f2d20d3768af15601f/92b2a/keyboard-shortcuts-for-pagination-2.jpg 810w, /static/06cd3247f1bd70f2d20d3768af15601f/fb816/keyboard-shortcuts-for-pagination-2.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/06cd3247f1bd70f2d20d3768af15601f/92b2a/keyboard-shortcuts-for-pagination-2.jpg" alt="Previous and Next links" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>navigation<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pagination<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-prev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prev<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Previous page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Previous page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Prev<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-next<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>next<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Next page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Next page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Next<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span></code></pre></div> <h3 id="numbered-1-2-3--previous-and-next-first-and-last-links" style="position:relative;"><a href="#numbered-1-2-3--previous-and-next-first-and-last-links" aria-label="numbered 1 2 3 previous and next first and last links permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Numbered (1, 2, 3, …), Previous and Next, First and Last links</h3> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/keyboard-shortcuts-for-pagination-3.jpg?align=overflow" data-alt="Numbered (1, 2, 3, &#x2026;), Previous and Next, First and Last links" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.586206896551722%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAED/9oADAMBAAIQAxAAAAHfS6gf/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAMT/9oACAEBAAEFAqKKqKqP/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BiP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAAAAyof/aAAgBAQAGPwJYJBIf/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAER8IH/2gAIAQEAAT8hcnJ5MFMFP//aAAwDAQACAAMAAAAQh9//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxCEP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAQACAQUBAAAAAAAAAAAAAAEAEfAhMUFRYaH/2gAIAQEAAT8QcCpNG2vsKd2PsOduPc//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/decff8c011350e51bc011f6dd42f62c4/3466e/keyboard-shortcuts-for-pagination-3.webp 203w, /static/decff8c011350e51bc011f6dd42f62c4/d7d9a/keyboard-shortcuts-for-pagination-3.webp 405w, /static/decff8c011350e51bc011f6dd42f62c4/6da5f/keyboard-shortcuts-for-pagination-3.webp 810w, /static/decff8c011350e51bc011f6dd42f62c4/a6361/keyboard-shortcuts-for-pagination-3.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/decff8c011350e51bc011f6dd42f62c4/37414/keyboard-shortcuts-for-pagination-3.jpg 203w, /static/decff8c011350e51bc011f6dd42f62c4/32fb1/keyboard-shortcuts-for-pagination-3.jpg 405w, /static/decff8c011350e51bc011f6dd42f62c4/92b2a/keyboard-shortcuts-for-pagination-3.jpg 810w, /static/decff8c011350e51bc011f6dd42f62c4/fb816/keyboard-shortcuts-for-pagination-3.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/decff8c011350e51bc011f6dd42f62c4/92b2a/keyboard-shortcuts-for-pagination-3.jpg" alt="Numbered (1, 2, 3, &#x2026;), Previous and Next, First and Last links" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>navigation<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pagination<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>First page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>First page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-prev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prev<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Previous page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Previous page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Prev<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number pagination-current<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 1, current page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1, current page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Page 5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>5<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-next<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>next<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Next page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Next page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Next<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pagination-last<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Last page<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Last page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="javascript" style="position:relative;"><a href="#javascript" aria-label="javascript permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript</h2> <p>I wrote a lightweight plugin to handle this part. You will love this – there are two versions of the plugin:</p> <ol> <li>Without dependencies: <strong><a href="https://v3.osvaldas.info/examples/keyboard-shortcuts-for-pagination/keyboard-pagination.js" target="_blank" rel="nofollow noopener noreferrer">keyboard-pagination.js</a>;</strong> (2kb when minified);</li> <li>With jQuery dependency: <strong><a href="https://v3.osvaldas.info/examples/keyboard-shortcuts-for-pagination/jquery.keyboard-pagination.js" target="_blank" rel="nofollow noopener noreferrer">jquery.keyboard-pagination.js</a></strong>; (1kb when minified);</li> </ol> <p>The plugin accepts some <strong>options</strong>:</p> <table> <thead> <tr> <th>Name</th> <th>Default value</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>num</td> <td>false</td> <td>List items selector for numbered (1, 2, 3, …) page links</td> </tr> <tr> <td>numCurrent</td> <td>false</td> <td>Currently active numbered list item selector</td> </tr> <tr> <td>prev</td> <td>false</td> <td>List item selector for the previous page link</td> </tr> <tr> <td>next</td> <td>false</td> <td>List item selector for the next page link</td> </tr> <tr> <td>first</td> <td>false</td> <td>List item selector for the first page link</td> </tr> <tr> <td>last</td> <td>false</td> <td>List item selector for the last page link</td> </tr> <tr> <td>doublePressInt</td> <td>250</td> <td>An interval (delta) in milliseconds between two button presses. Serves as a shortcut for first and last, which must be set. The lower the value the faster the button should be pressed two times.</td> </tr> <tr> <td>keyCodeLeft</td> <td>37</td> <td>Key code for previous page</td> </tr> <tr> <td>keyCodeRight</td> <td>39</td> <td>Key code for next page</td> </tr> </tbody> </table> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// non-jQuery</span> <span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">num</span><span class="token operator">:</span> <span class="token string">'.pagination-number'</span><span class="token punctuation">,</span> <span class="token literal-property property">numCurrent</span><span class="token operator">:</span> <span class="token string">'.pagination-current'</span><span class="token punctuation">,</span> <span class="token literal-property property">prev</span><span class="token operator">:</span> <span class="token string">'.pagination-prev'</span><span class="token punctuation">,</span> <span class="token literal-property property">next</span><span class="token operator">:</span> <span class="token string">'.pagination-next'</span><span class="token punctuation">,</span> <span class="token literal-property property">first</span><span class="token operator">:</span> <span class="token string">'.pagination-first'</span><span class="token punctuation">,</span> <span class="token literal-property property">last</span><span class="token operator">:</span> <span class="token string">'.pagination-last'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// jQuery</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">num</span><span class="token operator">:</span> <span class="token string">'.pagination-number'</span><span class="token punctuation">,</span> <span class="token literal-property property">numCurrent</span><span class="token operator">:</span> <span class="token string">'.pagination-current'</span><span class="token punctuation">,</span> <span class="token literal-property property">prev</span><span class="token operator">:</span> <span class="token string">'.pagination-prev'</span><span class="token punctuation">,</span> <span class="token literal-property property">next</span><span class="token operator">:</span> <span class="token string">'.pagination-next'</span><span class="token punctuation">,</span> <span class="token literal-property property">first</span><span class="token operator">:</span> <span class="token string">'.pagination-first'</span><span class="token punctuation">,</span> <span class="token literal-property property">last</span><span class="token operator">:</span> <span class="token string">'.pagination-last'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>The plugin has a two <strong>public methods</strong>:</p> <table> <thead> <tr> <th>Name</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>pause</td> <td>Stops the plugin</td> </tr> <tr> <td>resume</td> <td>Resumes the plugin</td> </tr> </tbody> </table> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// non-jQuery</span> <span class="token keyword">var</span> pagination <span class="token operator">=</span> <span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token comment">/* options */</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pagination<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pagination<span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// jQuery</span> <span class="token keyword">var</span> $pagination <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token comment">/* options */</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $pagination<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $pagination<span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="tips" style="position:relative;"><a href="#tips" aria-label="tips permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Tips</h2> <p>Only specify what is necessary. You do not have to set values for every selector option.</p> <p>If your pagination consists only of Previous / Next links, you only have to set the values for prev and next, for example:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">prev</span><span class="token operator">:</span> <span class="token string">'.pagination-prev'</span><span class="token punctuation">,</span> <span class="token literal-property property">next</span><span class="token operator">:</span> <span class="token string">'.pagination-next'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>If your pagination only has numbered (1, 2, 3, …) links, you only have to set the values for <code class="language-text">num</code> and <code class="language-text">numCurrent</code>, for example:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">keyboardPagination</span><span class="token punctuation">(</span><span class="token string">'.pagination'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">num</span><span class="token operator">:</span> <span class="token string">'.pagination-number'</span><span class="token punctuation">,</span> <span class="token literal-property property">numCurrent</span><span class="token operator">:</span> <span class="token string">'.pagination-current'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>If you have a pagination with both types of Previous / Next and 1, 2, 3, … links, you should specify only one group of options, either <code class="language-text">prev</code> and <code class="language-text">next</code> or <code class="language-text">num</code> and <code class="language-text">numCurrent</code>. There is no need for both groups.</p> <p>If you have First and Last links in your pagination and prefer enabling double press shortcut for arrow buttons, specify first and last, for example:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">keyboardPagination</span><span class="token punctuation">(</span> <span class="token string">'.pagination'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">first</span><span class="token operator">:</span> <span class="token string">'.pagination-first'</span><span class="token punctuation">,</span> <span class="token literal-property property">last</span><span class="token operator">:</span> <span class="token string">'.pagination-last'</span><span class="token punctuation">,</span> <span class="token literal-property property">num</span><span class="token operator">:</span> <span class="token string">'.pagination-number'</span><span class="token punctuation">,</span> <span class="token literal-property property">numCurrent</span><span class="token operator">:</span> <span class="token string">'.pagination-current'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>See how it works and study the real-life code:</p> <p><a href="https://v3.osvaldas.info/examples/keyboard-shortcuts-for-pagination/" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <h2 id="compatibility" style="position:relative;"><a href="#compatibility" aria-label="compatibility permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Compatibility</h2> <p>The jQuery version of the plugin is compatible with both 1.x (IE6+) and 2.x (IE9+) library branches. The non-jQuery version functions in Internet Explorer 8+. All fine with the other browsers.</p><![CDATA[Introducing Readerrr]]>https://osvaldas.info/introducing-readerrrhttps://osvaldas.info/introducing-readerrrWed, 26 Feb 2014 00:00:00 GMT<p>I love RSS. I think it is the best method to get the news. Way better than newsletters or social media. Why? I will dedicate one of my further posts to answer this question. So, because of my huge love for RSS, my heart was crying when I heard the sad, sad news: Google Reader is shutting down on July 1, 2013. The very first question that came into my mind was: <em>"I have lots of coffee supplies, how will I follow my favorite websites then?!"</em>.</p> <p>I started looking for the alternatives, but none of them was what I expected. Most of them did not differ much from MS Word, therefore they were not available on mobile devices. Others were slow and complex. They had no focus on design and UX. They did not have these tiny little tricks that make reading joyful. Even though today there are few good RSS readers that I would probably use, but they were too late – at the end of May 2013 I decided to do it myself (yeah, that’s my curse) and started wireframing…</p> <p>Two or maybe three months passed by and I had the first fully working version of my RSS reader. I could do all these essential things such as subscribing to new feeds, managing current subscriptions, sorting them, marking content automatically as read, etc. The design and development process was quite exciting and involving. I was building features one after another, fixing bugs, testing on various mobile devices. The result gradually took a state of potentially shippable product. I had this cautious idea at the early stage of building it, that it would be great to go public one fine day. And it’s today. I named it – Readerrr.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/introducing-readerrr-1.jpg?align=overflow" data-alt="Introducing Readerrr" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 62.06896551724138%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUCAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABdGSuGJEr/8QAGhABAAIDAQAAAAAAAAAAAAAAAQACAwQRI//aAAgBAQABBQJSoWnJsnhiy2vYOH//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAEAAgMAAAAAAAAAAAAAAAABABECECH/2gAIAQEABj8C6hvJlMqf/8QAGxAAAwADAQEAAAAAAAAAAAAAAAERITFhQaH/2gAIAQEAAT8hrFHRTcbXBpdEMq4f0wvl8JKaR//aAAwDAQACAAMAAAAQqM//xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAIAQMBAT8QiY//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCn/8QAGhABAQADAQEAAAAAAAAAAAAAAREAITFhUf/aAAgBAQABPxBeVyqgXmbUCwfcoaVw6iTueQcwt7ihNPuBaQhW5//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/bdff5b1162284d6288e186b2337e16e6/3466e/introducing-readerrr-1.webp 203w, /static/bdff5b1162284d6288e186b2337e16e6/d7d9a/introducing-readerrr-1.webp 405w, /static/bdff5b1162284d6288e186b2337e16e6/6da5f/introducing-readerrr-1.webp 810w, /static/bdff5b1162284d6288e186b2337e16e6/a6361/introducing-readerrr-1.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/bdff5b1162284d6288e186b2337e16e6/37414/introducing-readerrr-1.jpg 203w, /static/bdff5b1162284d6288e186b2337e16e6/32fb1/introducing-readerrr-1.jpg 405w, /static/bdff5b1162284d6288e186b2337e16e6/92b2a/introducing-readerrr-1.jpg 810w, /static/bdff5b1162284d6288e186b2337e16e6/fb816/introducing-readerrr-1.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/bdff5b1162284d6288e186b2337e16e6/92b2a/introducing-readerrr-1.jpg" alt="Introducing Readerrr" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p><strong>Readerrr is a RSS reader.</strong> A place where you can follow your favorite websites. Just that. Nothing less and nothing more. Narrow enough focus zone, but with its own specific problems to solve. I will review some of them here.</p> <h2 id="built-for-everyone-balanced-for-web-designers" style="position:relative;"><a href="#built-for-everyone-balanced-for-web-designers" aria-label="built for everyone balanced for web designers permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Built for everyone, balanced for web designers</h2> <p>Nothing unexpected: it’s simply because I am a web designer, front-end developer and because I built it firstly for myself. As a result I implemented <strong>syntax highlighter</strong>. For now, that’s the only (but probably the most important) feature specifically useful for website makers, but I have more in my plans. One of my favorite features on Readerrr.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/introducing-readerrr-2.jpg?align=overflow" data-alt="Readerrr&apos;s Syntax Highlighter" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 54.679802955665025%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUD/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB0tRLgB//xAAWEAADAAAAAAAAAAAAAAAAAAAEEDP/2gAIAQEAAQUCBogKL//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAAAIQcf/aAAgBAQAGPwIoyj//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAE/IVVf/9oADAMBAAIAAwAAABBDD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAICAgMAAAAAAAAAAAAAAAABEXEhMUGRsf/aAAgBAQABPxBE8cY6sSvsgZuv6cjZ/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/cd785b186ee781eda8dc6b3a830e47ef/3466e/introducing-readerrr-2.webp 203w, /static/cd785b186ee781eda8dc6b3a830e47ef/d7d9a/introducing-readerrr-2.webp 405w, /static/cd785b186ee781eda8dc6b3a830e47ef/6da5f/introducing-readerrr-2.webp 810w, /static/cd785b186ee781eda8dc6b3a830e47ef/a6361/introducing-readerrr-2.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/cd785b186ee781eda8dc6b3a830e47ef/37414/introducing-readerrr-2.jpg 203w, /static/cd785b186ee781eda8dc6b3a830e47ef/32fb1/introducing-readerrr-2.jpg 405w, /static/cd785b186ee781eda8dc6b3a830e47ef/92b2a/introducing-readerrr-2.jpg 810w, /static/cd785b186ee781eda8dc6b3a830e47ef/fb816/introducing-readerrr-2.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/cd785b186ee781eda8dc6b3a830e47ef/92b2a/introducing-readerrr-2.jpg" alt="Readerrr&apos;s Syntax Highlighter" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="reading-experience-and-ui" style="position:relative;"><a href="#reading-experience-and-ui" aria-label="reading experience and ui permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Reading experience and UI</h2> <p>RSS reader’s UX = reading experience which depends a lot on how user interface works. Readerrr has a fully scalable and zoomable UI. You can zoom in / out using default browser’s shortcuts (Cmd / Ctrl and + / -) or change default browser’s font size (in most cases default is 16px). This is super useful if you want to sit back and read your feeds from the distance or have weak eyesight.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/introducing-readerrr-3.jpg?align=overflow" data-alt="Introducing Readerrr" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 64.03940886699507%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB3BeIbKlf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAECEBIh/9oACAEBAAEFAst0bUT1f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABkQAAIDAQAAAAAAAAAAAAAAAAEQACGRMf/aAAgBAQAGPwJ0Rk6MX//EABoQAQEAAgMAAAAAAAAAAAAAAAEAESFBYZH/2gAIAQEAAT8hsncXGHqBdrG5W//aAAwDAQACAAMAAAAQbM//xAAWEQEBAQAAAAAAAAAAAAAAAAARECH/2gAIAQMBAT8QMWf/xAAWEQEBAQAAAAAAAAAAAAAAAAARECH/2gAIAQIBAT8QdJ//xAAdEAACAgEFAAAAAAAAAAAAAAABEQBBIWGBwdHw/9oACAEBAAE/EAQMM+3guaXTTuGFZm+yCignTzMAG6E//9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/37da8601bf2ed465c5758038e028d50c/3466e/introducing-readerrr-3.webp 203w, /static/37da8601bf2ed465c5758038e028d50c/d7d9a/introducing-readerrr-3.webp 405w, /static/37da8601bf2ed465c5758038e028d50c/6da5f/introducing-readerrr-3.webp 810w, /static/37da8601bf2ed465c5758038e028d50c/a6361/introducing-readerrr-3.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/37da8601bf2ed465c5758038e028d50c/37414/introducing-readerrr-3.jpg 203w, /static/37da8601bf2ed465c5758038e028d50c/32fb1/introducing-readerrr-3.jpg 405w, /static/37da8601bf2ed465c5758038e028d50c/92b2a/introducing-readerrr-3.jpg 810w, /static/37da8601bf2ed465c5758038e028d50c/fb816/introducing-readerrr-3.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/37da8601bf2ed465c5758038e028d50c/92b2a/introducing-readerrr-3.jpg" alt="Introducing Readerrr" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>There is more to UI: it is simple and clear. I believe in functional, distinctive and reasonable design. I also believe in this quote by Jonathan Ive. I think it perfectly sums up the theory of "good design":</p> <blockquote> <p>An indicator has a value when it’s indicating something, but if it’s not indicating something, it shouldn’t be there.</p> </blockquote> <h2 id="responsiveness" style="position:relative;"><a href="#responsiveness" aria-label="responsiveness permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Responsiveness</h2> <p>Web designer’s bread. I have tested Readerrr on <strong>iOS 6+, Android 4+ and Windows Phone 7+</strong>. I have plans to extend this circle of trinity a little bit in the nearest future.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; " data-url="../../images/blog/introducing-readerrr-4.jpg?align=overflow" data-alt="Introducing Readerrr" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 62.5615763546798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7VQo1EV//8QAGRABAQADAQAAAAAAAAAAAAAAAQIAEBES/9oACAEBAAEFAmwrppkrPBzP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhABAAMAAwAAAAAAAAAAAAAAAQAQMQIRIf/aAAgBAQAGPwI4us2jszID7X//xAAcEAACAgIDAAAAAAAAAAAAAAABEQAQITFBUWH/2gAIAQEAAT8hZ5wzFp1RomEn4hNqhea//9oADAMBAAIAAwAAABBoD//EABYRAAMAAAAAAAAAAAAAAAAAAAEQYf/aAAgBAwEBPxAVf//EABcRAAMBAAAAAAAAAAAAAAAAAAEQEUH/2gAIAQIBAT8QNxf/xAAaEAEAAwEBAQAAAAAAAAAAAAABACExEVFh/9oACAEBAAE/EHeHXNV9l5QB08vy47AFdqdXpEZRyOmLc//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/8736d7374ca64e62bdae236a11042dc5/3466e/introducing-readerrr-4.webp 203w, /static/8736d7374ca64e62bdae236a11042dc5/d7d9a/introducing-readerrr-4.webp 405w, /static/8736d7374ca64e62bdae236a11042dc5/6da5f/introducing-readerrr-4.webp 810w, /static/8736d7374ca64e62bdae236a11042dc5/a6361/introducing-readerrr-4.webp 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/webp"> <source srcset="/static/8736d7374ca64e62bdae236a11042dc5/37414/introducing-readerrr-4.jpg 203w, /static/8736d7374ca64e62bdae236a11042dc5/32fb1/introducing-readerrr-4.jpg 405w, /static/8736d7374ca64e62bdae236a11042dc5/92b2a/introducing-readerrr-4.jpg 810w, /static/8736d7374ca64e62bdae236a11042dc5/fb816/introducing-readerrr-4.jpg 960w" sizes="(max-width: 810px) 100vw, 810px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/8736d7374ca64e62bdae236a11042dc5/92b2a/introducing-readerrr-4.jpg" alt="Introducing Readerrr" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>On much larger screens there is a logical, mathematically calculated (woah!) break-point at which the content is centered relatively to the browser’s window, not sidebar. Keep your look straight, there’s no need to position browser’s window.</p> <p>If you are an iOS user, I strongly suggest you adding Readerrr to Home Screen because fortunately it won’t open Safari when you tap a link as it does in 99.9% cases. Readerrr will function in fullscreen mode like a normal app without downloading and installing anything.</p> <h2 id="keyboard-shortcuts" style="position:relative;"><a href="#keyboard-shortcuts" aria-label="keyboard shortcuts permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Keyboard shortcuts</h2> <p>Doing repetitive actions with mouse or touchpad is annoying. Here come keyboard shortcuts to ease the life: skipping through posts, switching through subscriptions, entering fullscreen mode, etc.</p> <p>My favorite one is Space (spacebar). I redesigned the default functionality: instead of just scrolling down the page at 100%, it does that at 90% and marks the place where you’ve been reading with a red line. Quite practical, isn’t it?</p> <p><img src="/56c5d512c0ec90a3c97769894ce378c6/introducing-readerrr-5.gif" alt="Introducing Readerrr"></p> <h2 id="paid-but-no-free-trial" style="position:relative;"><a href="#paid-but-no-free-trial" aria-label="paid but no free trial permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Paid, but no free-trial?</h2> <p>The majority of the best online products and services are paid. That’s how business owners can guarantee quality. I am extremely skeptical about ads (evil) and quite skeptical about freemium (confusing) monetization types. I chose to be straight and simple in all the ways, not excluding the pricing.</p> <p>The business decision I took is based on a consistent and responsible growth. I want to limit the amount of short-term users, and thus achieve the highest possible amount of users who are not too happy with their current RSS reader. At some point this is similar to invite-only strategy which is based on the same goal: consistent and responsible growth. Because bubbles burst!</p> <h2 id="future" style="position:relative;"><a href="#future" aria-label="future permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Future</h2> <p>I have tons of improvement and feature ideas for Readerrr. I will consistently make them happen. I will also share the process, discoveries of growing a product as well as front-end and design techniques here, on my personal blog. I am going to announce a new feature in a couple of weeks!</p> <p>P.S. did you know that Google Reader would never tell you which of your subscriptions were broken because of dead RSS links? I discovered that after importing my huge base of subscriptions to Readerrr. These were like the cows in your list that silently stopped giving you milk. Readerrr marks these entries and advices what you should do!</p><![CDATA[Image Lightbox, Responsive and Touch‑friendly]]>https://osvaldas.info/image-lightbox-responsive-touch-friendlyhttps://osvaldas.info/image-lightbox-responsive-touch-friendlyMon, 27 Jan 2014 00:00:00 GMT<p>As for the image lightboxes, I have always lacked simplicity for them. I mean not only the visual design, but the overall experience: from implementation to <abbr title="User Experience">UX</abbr>. All due respect, but I have never liked any of these <em>light-fancy-face-boxes</em>. They all were solving problems that just <a href="http://www.rachelandrew.co.uk/archives/2012/03/21/stop-solving-problems-you-dont-yet-have" target="_blank" rel="nofollow noopener noreferrer">don’t exist</a>: lots of unwanted features by default, total mess in markup, no native behavior for touch screens. I was working on a project and needed an image lightbox that solves these problems. I needed a lightbox only for images, not video, text, and iframes at once. I made <strong>ImageLightbox.js</strong>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/image-lightbox-responsive-touch-friendly-1.jpg" data-alt="Image Lightbox, Responsive and Touch&#x2011;friendly"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.70935960591133%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFAf/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAGsps4qgYU//8QAGRABAAIDAAAAAAAAAAAAAAAAAQIxEBES/9oACAEBAAEFAmomdcpX/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/AQ2f/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAQUBAAAAAAAAAAAAAAAAEAACESFBgf/aAAgBAQAGPwJb0w2h/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAERITFxof/aAAgBAQABPyHfhY/DSQO1DEmYlmfD/9oADAMBAAIAAwAAABBYH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QB//EABYRAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxBcn//EABwQAQADAAIDAAAAAAAAAAAAAAEAESEQMWGhwf/aAAgBAQABPxDCnYn1MNbgWJDvyuAE0EpIhC3A8MSqtqJ//9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/c7357eeb39a679cad46b34d2086bb49f/3466e/image-lightbox-responsive-touch-friendly-1.webp 203w, /static/c7357eeb39a679cad46b34d2086bb49f/d7d9a/image-lightbox-responsive-touch-friendly-1.webp 405w, /static/c7357eeb39a679cad46b34d2086bb49f/7df04/image-lightbox-responsive-touch-friendly-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/c7357eeb39a679cad46b34d2086bb49f/37414/image-lightbox-responsive-touch-friendly-1.jpg 203w, /static/c7357eeb39a679cad46b34d2086bb49f/32fb1/image-lightbox-responsive-touch-friendly-1.jpg 405w, /static/c7357eeb39a679cad46b34d2086bb49f/a6335/image-lightbox-responsive-touch-friendly-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/c7357eeb39a679cad46b34d2086bb49f/a6335/image-lightbox-responsive-touch-friendly-1.jpg" alt="Image Lightbox, Responsive and Touch&#x2011;friendly" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="features" style="position:relative;"><a href="#features" aria-label="features permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Features</h2> <ol> <li><strong>Ascetic.</strong> No captions, navigation buttons or background cover by default. Nothing that would distract user from the main purpose. That’s why I enjoy pointing out <a href="http://vimeo.com/7827217" target="_blank" rel="nofollow noopener noreferrer">Jony Ive’s observation</a>: <em>“An indicator has a value when it’s indicating something, but if it’s not indicating something, it shouldn’t be there”</em>. I think it is the most common thing that designers forget to solve.</li> <li><strong>Minimalistic.</strong> No bunch of default raster image files that fail on higher resolution screens. Just one source file which is only <strong>4kb</strong> in size when minified. No messy markup. Just one simple element – <code class="language-text">&lt;img></code>.</li> <li><strong>Extensible &#x26; configurable.</strong> If the default functionality is not enough, you can easily extend the plugin with custom JavaScript functions, change the settings or use a couple of useful method functions.</li> <li><strong>Responsive and touch-friendly.</strong> The most trending topics in web design and they are here. Images fit to any screen size and are swipe-able (native behavior) on touch capable devices.</li> <li><strong>iOS, Android and Windows Phone compatible.</strong> As well as desktop versions of Safari, Chrome, Firefox, Opera and Internet Explorer.</li> <li><strong>jQuery 1.x, 2.x, 3.x compatible.</strong> Just saying.</li> <li><strong>Preloads next image.</strong> While user is viewing the current picture, the plugin silently preloads the next picture which shows up without any delay when respective action is triggered.</li> <li><em>Uses CSS transform and transition for moving images.</em> Turns out CSS’s <a href="http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft" target="_blank" rel="nofollow noopener noreferrer"><code class="language-text">transform</code> performs better</a> than <code class="language-text">left</code> (as well as <code class="language-text">right</code>, <code class="language-text">top</code>, <code class="language-text">bottom</code>). But the plugin falls back on left if a browser does not support <code class="language-text">transform</code>.</li> <li><strong>Interacts with keyboard.</strong> Standard, but essential Arrow Left, Arrow Right to switch images and Esc to quit the lightbox.</li> </ol> <h2 id="html" style="position:relative;"><a href="#html" aria-label="html permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML</h2> <p>As I mentioned earlier, the plugin produces a single-element markup by default:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>picture.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>imagelightbox<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre></div> <h2 id="css" style="position:relative;"><a href="#css" aria-label="css permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS</h2> <p>No default CSS. Everything is up to you. The minimal configuration I recommend is:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#imagelightbox</span><span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 9999<span class="token punctuation">;</span> <span class="token property">-ms-touch-action</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">touch-action</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p><code class="language-text">position</code> value can be anything, but in most cases <code class="language-text">fixed</code> will produce what’s expected. Configure <code class="language-text">z-index</code> to anything that fits your context best. <code class="language-text">touch-action: none</code> removes the default touch manipulation from the element.</p> <h2 id="javascript" style="position:relative;"><a href="#javascript" aria-label="javascript permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript</h2> <p>Source: <a href="https://v3.osvaldas.info/examples/image-lightbox-responsive-touch-friendly/imagelightbox.js" target="_blank" rel="nofollow noopener noreferrer">imagelightbox.js</a> (uncompressed; 9kb) and <a href="https://v3.osvaldas.info/examples/image-lightbox-responsive-touch-friendly/imagelightbox.min.js" target="_blank" rel="nofollow noopener noreferrer">imagelightbox.min.js</a> (minified; 4kb).</p> <p>The plugin takes the advantage of jQuery. That means the plugin code or file must follow jQuery code or file. An example of the basic implementation:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>imagelightbox.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">imageLightbox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p><code class="language-text">ImageLightbox()</code> accepts some options. The defaults are:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">imageLightbox</span><span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token literal-property property">selector</span><span class="token operator">:</span> <span class="token string">'id="imagelightbox"'</span><span class="token punctuation">,</span> <span class="token comment">// string;</span> <span class="token literal-property property">allowedTypes</span><span class="token operator">:</span> <span class="token string">'png|jpg|jpeg|gif'</span><span class="token punctuation">,</span> <span class="token comment">// string;</span> <span class="token literal-property property">animationSpeed</span><span class="token operator">:</span> <span class="token number">250</span><span class="token punctuation">,</span> <span class="token comment">// integer;</span> <span class="token literal-property property">preloadNext</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// bool; silently preload the next image</span> <span class="token literal-property property">enableKeyboard</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// bool; enable keyboard shortcuts (arrows Left/Right and Esc)</span> <span class="token literal-property property">quitOnEnd</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// bool; quit after viewing the last image</span> <span class="token literal-property property">quitOnImgClick</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// bool; quit when the viewed image is clicked</span> <span class="token literal-property property">quitOnDocClick</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// bool; quit when anything but the viewed image is clicked</span> <span class="token literal-property property">onStart</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// function/bool; calls function when the lightbox starts</span> <span class="token literal-property property">onEnd</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// function/bool; calls function when the lightbox quits</span> <span class="token literal-property property">onLoadStart</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// function/bool; calls function when the image load begins</span> <span class="token literal-property property">onLoadEnd</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token comment">// function/bool; calls function when the image finishes loading</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>It also has a couple of method functions, as mentioned:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">var</span> $instance <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>selector<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">imageLightbox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $instance<span class="token punctuation">.</span><span class="token function">switchImageLightbox</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// switches to the other image; accepts integer argument (an index of the desired image)</span> $instance<span class="token punctuation">.</span><span class="token function">quitImageLightbox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// quits the lightbox</span> $instance<span class="token punctuation">.</span><span class="token function">addToImageLightbox</span><span class="token punctuation">(</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'a.is-new'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// adds new image</span></code></pre></div> <h2 id="examples" style="position:relative;"><a href="#examples" aria-label="examples permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Examples</h2> <p>Plugin without demo is a rhyme without reason. I have crafted a few practical examples how ImageLightbox and its options can be used in real life.</p> <p><a href="https://v3.osvaldas.info/examples/image-lightbox-responsive-touch-friendly" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <p>You can contribute, follow the project on <a href="https://github.com/osvaldasvalutis/imageLightbox.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a>.</p> <p>The most interesting part in making of the plugin was mastering touch events on Windows Phone. That’s what the next post will probably be about.</p> <p>P.S. I have recently discovered <a href="http://codepen.io/terrymun/full/JKHwp" target="_blank" rel="nofollow noopener noreferrer">Fluidbox</a> which is another approach to view images and, which I liked about it most, basically matches the design principles I’ve mentioned at the beginning of the post.</p> <h2 id="changelog" style="position:relative;"><a href="#changelog" aria-label="changelog permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Changelog</h2> <ul> <li>The plugin now is on <a href="https://github.com/osvaldasvalutis/imageLightbox.js" target="_blank" rel="nofollow noopener noreferrer">GitHub</a> too <em>(2016-11-22)</em>;</li> <li>Support for jQuery 3.x and <code class="language-text">addToImageLightbox</code> function <em>(2016-06-17)</em>;</li> <li>​Added arrow buttons navigation <a href="https://v3.osvaldas.info/examples/image-lightbox-responsive-touch-friendly/#nav" target="_blank" rel="nofollow noopener noreferrer">example</a> in the demo page <em>(2014-07-22)</em>.</li> </ul><![CDATA[Imitating calc() Fallback or Fixed-Width Sidebar In Responsive Layout]]>https://osvaldas.info/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layouthttps://osvaldas.info/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layoutMon, 02 Dec 2013 00:00:00 GMT<p>Dealing with CSS browser compatibility and solving technical issues is probably the real charm (I mean it) of front-end development. None of the projects do without it. Working on one of those I finally had my first experience with CSS function <code class="language-text">calc()</code>:</p> <blockquote> <p>With <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc" target="_blank" rel="nofollow noopener noreferrer">calc()</a>, you can perform calculations to determine CSS property values.</p> </blockquote> <p>I know that you know this, and I also know that you have already seen these <a href="http://www.w3.org/TR/css3-values/#calc-notation" target="_blank" rel="nofollow noopener noreferrer">examples</a> and have even tried them at home:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2.5em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">background-position</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% + 50px<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>1.25rem - 5px<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>As you see, calc() takes writing CSS to much higher level. It is so powerful that front-enders have no idea what to do with it yet. And with the recent appearance of KitKat (Android 4.4) the function has become <a href="http://caniuse.com/#feat=calc" target="_blank" rel="nofollow noopener noreferrer">supported in all major browsers</a> (sorry 3rd brother, Opera Mini). Start using it, wisely.</p> <h2 id="situation" style="position:relative;"><a href="#situation" aria-label="situation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Situation</h2> <p>Imagine this painfully standard situation: content and sidebar areas floated side by side, separated by gap:</p> <p><img src="/a4dd477414e7437b0c93fe932cffad6d/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layout-1.gif" alt="Imitating calc() Fallback or Fixed-Width Sidebar In Responsive Layout"></p> <p>Usually you have two <code class="language-text">div</code>’s wrapped into one and styled:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Content<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sidebar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sidebar<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.container, .content, .sidebar</span> <span class="token punctuation">{</span> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.container</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">max-width</span><span class="token punctuation">:</span> 80em<span class="token punctuation">;</span> <span class="token comment">/* 1280 */</span> <span class="token property">padding</span><span class="token punctuation">:</span> 2.5em<span class="token punctuation">;</span> <span class="token comment">/* 40 */</span> <span class="token punctuation">}</span> <span class="token selector">.content</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span> <span class="token comment">/* 960 */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.sidebar</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 16.666%<span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token punctuation">}</span> @media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 40em<span class="token punctuation">)</span> <span class="token comment">/* 640 */</span> <span class="token punctuation">{</span> <span class="token selector">.content, .sidebar</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.sidebar</span> <span class="token punctuation">{</span> <span class="token property">margin-top</span><span class="token punctuation">:</span> 1.25em<span class="token punctuation">;</span> <span class="token comment">/* 20 */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>Looks like a perfectly responsive layout and there is nothing left to add, however…</p> <h2 id="problem" style="position:relative;"><a href="#problem" aria-label="problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Problem</h2> <p><img src="/720d6294a2ad7e92ca22b5cd022867bb/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layout-2.gif" alt="Imitating calc() Fallback or Fixed-Width Sidebar In Responsive Layout"></p> <p>The layout I worked on had quite a small initial width of the sidebar and a wide content area. Therefore, when resizing down the viewport, the sidebar becomes extremely tight very quickly. What to do when the sidebar gets <strong>too narrow</strong> and moving it under the content is yet <strong>too early</strong>?</p> <h2 id="solution" style="position:relative;"><a href="#solution" aria-label="solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Solution</h2> <p><img src="/19e41211cec3593021cefedf64358cd1/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layout-3.gif" alt="Imitating calc() Fallback or Fixed-Width Sidebar In Responsive Layout"></p> <p>It is a fixed-width sidebar, calc(), and a fallback for browsers that do not support this function. A minor modification of CSS classes <code class="language-text">.content</code> and <code class="language-text">.sidebar</code> brings these three bolds to life:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.content</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span> <span class="token comment">/* 960 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 15em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 240 */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.sidebar</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 16.666%<span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>12.5em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>If you were attentive you should see that the line <code class="language-text">width: calc(12.5em)</code> is the trick here. Why? Let’s see all these widths like in pairs – exactly what we need browsers to do.</p> <p>The <em>first</em> pair is <code class="language-text">width: 80%</code> (content) and <code class="language-text">width: 16.666% </code>(sidebar) that does the job in calc() incompatible browsers, which will just ignore <code class="language-text">calc()</code>. It is the callback (backup). It means that the minority of website visitors will see narrower sidebar on narrower viewports. And that should be perfectly fine for people using older browser versions.</p> <p>The <em>second</em>: <code class="language-text">width: calc(100% - 15em)</code> and <code class="language-text">width: calc(12.5em)</code>. This is for newer browser versions which will override the previous width definitions. There are a couple of things to explain:</p> <ul> <li>Content: <code class="language-text">calc(100% - 15em)</code> = <em>100% - sidebar width - gap width</em>. Besides that this calculates the width of the content element, it also assures that the gap between the sidebar and content is always equal to <code class="language-text">2.5em</code> (40px). Total win for perfectionists.</li> <li>Sidebar: <code class="language-text">calc(12.5em)</code> – who said that you need to calculate something between the brackets. This little trick cheats browsers into seeing width properties in pairs and using the only understandable one for them. I called this “imitating calc() fallback”, but actually it is vice versa. Um…</li> </ul> <p>Party… Demo time! I made a button to imitate calc() support, you may want to try it:</p> <p><a href="https://v3.osvaldas.info/examples/imitating-calc-fallback-fixed-width-sidebar-in-responsive-layout" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <h2 id="one-more-thing" style="position:relative;"><a href="#one-more-thing" aria-label="one more thing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>One more thing</h2> <p>Just like many other CSS features, calc() was an experimental one and only available under the vendor prefixes some time ago. So let’s get the maximum of it:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.content</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span> <span class="token comment">/* 960 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">-webkit-calc</span><span class="token punctuation">(</span>100% - 15em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 240 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">-moz-calc</span><span class="token punctuation">(</span>100% - 15em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 240 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 15em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 240 */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.sidebar</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 16.666%<span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">-webkit-calc</span><span class="token punctuation">(</span>12.5em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">-moz-calc</span><span class="token punctuation">(</span>12.5em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>12.5em<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 200 */</span> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>There is a way to do without calc(), and that is by taking the advantage of CSS property <code class="language-text">position</code>. However, the whole layout would fail (which you do not want) if the sidebar was higher than the content area.</p><![CDATA[Drop-Down Navigation: Responsive and Touch-Friendly]]>https://osvaldas.info/drop-down-navigation-responsive-and-touch-friendlyhttps://osvaldas.info/drop-down-navigation-responsive-and-touch-friendlyTue, 02 Apr 2013 00:00:00 GMT<p>What if you need a multi-level navigation? In most cases, you design a drop-down menu using unordered lists. But what do you do to make it usable on small and / or cursorless screens? By usable I mean being able to use hyperlinks on parental anchors and open them with a double-tap (which is a native act on touch devices), also being able to close the drop-downs by tapping anywhere outside them to avoid flashing and other huge usability faults but having a usual bulletproof drop-down menu on desktop screens at the same time. A while ago I came up with quite a simple technique. I have been successfully implementing it into my projects as there is no room for one-sided techniques anymore.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/drop-down-navigation-touch-friendly-and-responsive-1.jpg" data-alt="Drop-Down Navigation: Responsive and Touch-Friendly"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 34.48275862068966%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHWgmrQn//EABcQAAMBAAAAAAAAAAAAAAAAAAABEhD/2gAIAQEAAQUCVCrP/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Bp//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/AYf/xAAZEAACAwEAAAAAAAAAAAAAAAABEgAQcYH/2gAIAQEABj8CLchY5X//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhMUFR/9oACAEBAAE/IWk0M5HkN9G9DkEn/9oADAMBAAIAAwAAABB8L//EABYRAAMAAAAAAAAAAAAAAAAAAAEQYf/aAAgBAwEBPxAwv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QH//EABsQAAMBAAMBAAAAAAAAAAAAAAERIQAxQVFx/9oACAEBAAE/EBgBmwn0dNo6k/qc5Sloprf/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/1d8d8a86104d0927e05548bc318f8ef7/3466e/drop-down-navigation-touch-friendly-and-responsive-1.webp 203w, /static/1d8d8a86104d0927e05548bc318f8ef7/d7d9a/drop-down-navigation-touch-friendly-and-responsive-1.webp 405w, /static/1d8d8a86104d0927e05548bc318f8ef7/7df04/drop-down-navigation-touch-friendly-and-responsive-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/1d8d8a86104d0927e05548bc318f8ef7/37414/drop-down-navigation-touch-friendly-and-responsive-1.jpg 203w, /static/1d8d8a86104d0927e05548bc318f8ef7/32fb1/drop-down-navigation-touch-friendly-and-responsive-1.jpg 405w, /static/1d8d8a86104d0927e05548bc318f8ef7/a6335/drop-down-navigation-touch-friendly-and-responsive-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/1d8d8a86104d0927e05548bc318f8ef7/a6335/drop-down-navigation-touch-friendly-and-responsive-1.jpg" alt="Drop-Down Navigation: Responsive and Touch-Friendly" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="the-problem" style="position:relative;"><a href="#the-problem" aria-label="the problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The Problem</h2> <p>...is a drop-down on touch screen because there is no cursor and you cannot hover on the navigation items! It gets even worse if you have links on parent menu items. Once you click them, the drop-down shows up for a second and then immediately disappears because the browser has just opened the link of an anchor you <a href="http://stuffandnonsense.co.uk/blog/about/not-click.-not-tap.-not-select.-press" target="_blank" rel="nofollow noopener noreferrer">pressed</a>! That sucks. As well as using hash symbol for parental link <code class="language-text">href</code>'s: as a consequence, you cannot use normal URLs, the page sometimes may scroll up after the link is clicked, the drop-down may flash, you won't be able to shut it off, etc. Sorry, that is not a problem anymore – it is solved in this post! Watch my very first video montage to get the idea:</p> <p> <div class="video-embed"> <iframe title="Drop-Down Navigation: Responsive and Touch-Friendly" width="560" height="316" src="https://player.vimeo.com/video/63104740" class="embedVideo-iframe" style="border:0" loading="eager" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups" ></iframe> </div></p> <p>Talking about responsiveness, there is a plenty, plenty of responsive navigation solutions out there. I admit, I have hardly gone deeper into any of these, but still my favorite one is David Bushell's <a href="http://coding.smashingmagazine.com/2013/01/15/off-canvas-navigation-for-responsive-website" target="_blank" rel="nofollow noopener noreferrer">off-canvas</a> navigation. It is awesome. However, as for my personal projects I fit the usual <em>one-button</em> responsive navigation <a href="http://bradfrostweb.com/blog/web/responsive-nav-patterns" target="_blank" rel="nofollow noopener noreferrer">pattern</a>. The same one goes here as well.</p> <h2 id="composition" style="position:relative;"><a href="#composition" aria-label="composition permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Composition</h2> <p>The technique consists of three main parts:</p> <ol> <li>Simple drop-down navigation based on HTML and CSS;</li> <li>Responsiveness implementation using media queries;</li> <li>Adoption for touch screen devices with the help of a super tiny jQuery plugin.</li> </ol> <p>Now about everything in turn…</p> <h2 id="html-and-css-drop-down-navigation" style="position:relative;"><a href="#html-and-css-drop-down-navigation" aria-label="html and css drop down navigation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML and CSS drop-down navigation</h2> <p>Firstly, simple markup of multi-leveled unordered lists with a little bit of HTML5 flavor. My example consists of two levels. Two is not the limit, you may have infinite levels on your next super project.</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>nav<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>navigation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#nav<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Show navigation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Show navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Hide navigation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Hide navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">aria-haspopup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Design<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>JavaScript<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">aria-haspopup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Work<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Web Design<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Typography<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Front-End<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span></code></pre></div> <p>Now let's just give a sense to the HTML above with the CSS below. Presenting only the principle parts of CSS. Full code – later in the demo.</p> <p>Do not be surprised by <em>Show / Hide navigation</em> anchors. I am going to hide these for now using <code class="language-text">#nav > a</code> selector and flick on later in the next step.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#nav</span> <span class="token punctuation">{</span> <span class="token comment">/* container */</span> <span class="token punctuation">}</span> <span class="token selector">#nav > a</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav li</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* first level */</span> <span class="token selector">#nav > ul</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 3.75em<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav > ul > li</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 25%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* second level */</span> <span class="token selector">#nav li ul</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav li:hover ul</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>User interface behavior on non-touch screens is based on the result of <code class="language-text">position: relative</code> and <code class="language-text">position: absolute</code> manipulation. On touchscreens it still fails at solving the problem, but we will make it right later in the 3<sup>rd</sup> part of the post.</p> <h2 id="responsive-drop-down-navigation" style="position:relative;"><a href="#responsive-drop-down-navigation" aria-label="responsive drop down navigation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Responsive drop-down navigation</h2> <p>Let's pour some responsiveness in. 640 pixels is my choice for a major breakpoint at which with the help of a single media query, the navigation shrinks to a button (icon). Therefore, the menu can only be called out by pressing the button.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css">@media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 40em<span class="token punctuation">)</span> <span class="token comment">/* 640 */</span> <span class="token punctuation">{</span> <span class="token selector">#nav</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav > a</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token selector">#nav:not(:target) > a:first-of-type, #nav:target > a:last-of-type</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* first level */</span> <span class="token selector">#nav > ul</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav:target > ul</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#nav > ul > li</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* second level */</span> <span class="token selector">#nav li ul</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> static<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>CSS pseudo selector :target makes it easy to toggle menu as the anchor's <code class="language-text">href</code> attribute coincides with CSS ID selector name. Therefore, as you might have understood, the first anchor of <code class="language-text">#nav > a</code> is responsible for showing and the second for hiding the menu.</p> <h2 id="touch-friendly-drop-down-navigation" style="position:relative;"><a href="#touch-friendly-drop-down-navigation" aria-label="touch friendly drop down navigation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Touch-friendly drop-down navigation</h2> <p>The most important things go in the end: let's finally solve the problem described in the very first paragraphs of the post. Here comes the promised super lightweight jQuery plugin which I called <strong><a href="https://v3.osvaldas.info/examples/drop-down-navigation-touch-friendly-and-responsive/doubletaptogo.js" target="_blank" rel="nofollow noopener noreferrer">DoubleTapToGo.js</a></strong> and it is only 550 ridiculous bytes in size when <a href="https://v3.osvaldas.info/examples/drop-down-navigation-touch-friendly-and-responsive/doubletaptogo.min.js" target="_blank" rel="nofollow noopener noreferrer">minified</a>.</p> <p>When you tap the parent item for the first time, DoubleTapToGo prevents the browser from opening a new URL but allows that if tapped once again in succession. If there is an intermediate tap(s) between the first one and the second the counter resets. Quite simple principle of operation, isn't it? It may be hard to believe but this solved all of the flaws I have mentioned before.</p> <p>Considering the markup above, the plugin should be only applied to the items that are parents – in order to avoid double-tap requirement on <em>drop-down-less</em> items:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span> <span class="token string">'#nav li:has(ul)'</span> <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">doubleTapToGo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>This is it! Complete fulfillment, full code and working example – all in the demo:</p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>I have ran quite successful tests on both Mac and Windows versions of the latest (April, 2013) Chrome, Safari, Firefox, Opera releases, also IE9+, iOS 6 / 7, Android 2.2 / 4.2 / 4.4, Windows Phone 7 / 7.5, Opera Mobile.</p> <h2 id="changelog" style="position:relative;"><a href="#changelog" aria-label="changelog permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Changelog</h2> <ul> <li>Windows Phone 7.5 is tricky when it comes to <code class="language-text">:hover</code>. Append <code class="language-text">aria-haspopup="true"</code> to the anchors that have <code class="language-text">&lt;ul></code> siblings. <em>(2014-01-29)</em></li> </ul><![CDATA[Audio Player: Responsive and Touch-Friendly]]>https://osvaldas.info/audio-player-responsive-and-touch-friendlyhttps://osvaldas.info/audio-player-responsive-and-touch-friendlyThu, 27 Dec 2012 00:00:00 GMT<p>A couple of months ago I built a jQuery plugin that replaces <code class="language-text">&lt;audio></code> element with a little of custom HTML code. By adding some CSS you get a whole new player which looks the way you want and has the same functionality as the default player. There is no direct way to style the element. But the HTML5 DOM has methods, properties, and events for the element and thus makes it quite easily manipulable.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/audio-player-responsive-and-touch-friendly-1.jpg?align=full" data-alt="Audio Player: Responsive and Touch-Friendly" data-align="full"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 50.2463054187192%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAE/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAdoclwj/xAAYEAACAwAAAAAAAAAAAAAAAAAAAQIRIP/aAAgBAQABBQJlyz//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAaEAAABwAAAAAAAAAAAAAAAAAAAhEgITOh/9oACAEBAAY/AoKor1v/xAAZEAADAAMAAAAAAAAAAAAAAAAAAREgsfD/2gAIAQEAAT8htPYhwUiw/9oADAMBAAIAAwAAABBML//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAMBAQEBAAAAAAAAAAAAAAABEVExIZH/2gAIAQEAAT8QdZSvrmPp79ApKqPCvSvSvT//2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/20178f61f4d897bc04cee164e9e5da1e/3466e/audio-player-responsive-and-touch-friendly-1.webp 203w, /static/20178f61f4d897bc04cee164e9e5da1e/d7d9a/audio-player-responsive-and-touch-friendly-1.webp 405w, /static/20178f61f4d897bc04cee164e9e5da1e/7df04/audio-player-responsive-and-touch-friendly-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/20178f61f4d897bc04cee164e9e5da1e/37414/audio-player-responsive-and-touch-friendly-1.jpg 203w, /static/20178f61f4d897bc04cee164e9e5da1e/32fb1/audio-player-responsive-and-touch-friendly-1.jpg 405w, /static/20178f61f4d897bc04cee164e9e5da1e/a6335/audio-player-responsive-and-touch-friendly-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/20178f61f4d897bc04cee164e9e5da1e/a6335/audio-player-responsive-and-touch-friendly-1.jpg" alt="Audio Player: Responsive and Touch-Friendly" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>To sum it up, <strong>the plugin enables indirect styling of <code class="language-text">&lt;audio></code> element</strong>. That's why I built it. I wanted the player to fit the design theme for one of my present projects – IP telephony for business (VOIP). The audio element serves for IVR (Interactive Voice Response) function where clients can built their own voice responders / menus, make audio records and rehear them.</p> <p>A couple of weeks ago I wrote an in-depth article about the plugin for <a href="http://tympanus.net/codrops/2012/12/04/responsive-touch-friendly-audio-player" target="_blank" rel="nofollow noopener noreferrer">Codrops</a>, which I recommend for people who think of themselves as less experienced in the field. Receiving some helpful feedback there, helped me to improve the player. Obviously, this is not the end and I will be looking for your feedback here as well.</p> <h2 id="features" style="position:relative;"><a href="#features" aria-label="features permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Features</h2> <p>Before deciding to build a plugin on my own, I did a little research on existing alternatives. I had clear and strict requirements: <strong>(1)</strong> it must be lightweight, customizable; <strong>(2)</strong> it must solve contemporary problems like touch and responsive; <strong>(3)</strong> it must not solve problems <a href="http://rachelandrew.co.uk/archives/2012/03/21/stop-solving-problems-you-dont-yet-have" target="_blank" rel="nofollow noopener noreferrer">I don't have yet</a>. However, nothing passed through the filter. As the matter of fact, the player I built is:</p> <ul> <li><strong>Responsive.</strong></li> </ul> <p>To achieve this, full responsibility falls on your CSS. My example is responsive. You can have a non-responsive player as well, but that is not recommended. Responsiveness is an important sign of a good web experience.</p> <ul> <li><strong>Touchable.</strong></li> </ul> <p><em>Touchableness</em> is an important sign of a good web experience as well. Having these two, you are device and screen independent. And that is a half job done!</p> <ul> <li><strong>Adaptive.</strong></li> </ul> <p>When the browser does not support the audio element entirely or any of the provided audio files, the player then gracefully degrades to a one-button (Play/Pause buttons only) <code class="language-text">&lt;embed /></code> element based player which will use a third party plugin (mostly Quick Time on Mac, Windows Media Player on Windows) to play the audio file. Another possible scenario: JavaScript disabled in the browser. Browser's default player takes action then. And that is totally fine.</p> <ul> <li><strong>Native.</strong></li> </ul> <p>The player is in no way directly native. The plugin accepts audio element's attributes (<code class="language-text">src</code>, <code class="language-text">autoplay</code>, <code class="language-text">loop</code>, <code class="language-text">preoload</code>) and tags (<code class="language-text">&lt;source></code>). Besides that, <code class="language-text">autoplay</code> and <code class="language-text">loop</code> attributes are inherited on the previously mentioned fallback case.</p> <ul> <li><strong>Usable.</strong></li> </ul> <p>The essential <em>Play / Pause</em> and playback progress controls, <em>Volume On / Off / Up / Down</em> controls and indication of how much of the audio is preloaded (buffered).</p> <ul> <li><strong>Image-less.</strong></li> </ul> <p>This depends on your CSS as well. The looks of my player is all CSS, not a single image file used. Having that and sizing everything with <code class="language-text">em</code> units, enables the player to be scaled and zoomed.</p> <ul> <li><strong>Flash-less.</strong></li> </ul> <p>So long, Flash. You are unwanted here.</p> <ul> <li><strong>Lightness.</strong></li> </ul> <p>The minified version of the plugin is just <strong>4KB</strong> large small.</p> <h2 id="usage" style="position:relative;"><a href="#usage" aria-label="usage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Usage</h2> <p>Add the audio element, set the attributes you need and add source(s). The more different sources you add, the more users will be able to listen to your audio (because none of the audio formats are supported across all browsers). Three examples:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token attr-name">preload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>audio</span><span class="token punctuation">></span></span></code></pre></div> <p>This will just preload the <code class="language-text">audio.wav</code> file and won't play it until user clicks Play button. Other <code class="language-text">preload</code> values (<code class="language-text">none</code>, <code class="language-text">metadata</code>) will not preload the file.</p> <p><code class="language-text">autoplay</code> the file when it loads and then <code class="language-text">loop</code> it in this way:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token attr-name">preload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span> <span class="token attr-name">autoplay</span> <span class="token attr-name">loop</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>audio</span><span class="token punctuation">></span></span></code></pre></div> <p>Specify multiple audio formats to solve the previously mentioned problem:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">preload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.mp3<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.ogg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>audio</span><span class="token punctuation">></span></span></code></pre></div> <p>Mystic <code class="language-text">controls</code>? It is a boolean attribute which does not influence the plugin in any way, but ensures that browser's default player is displayed and displayed with controls when JavaScript is disabled.</p> <p>The final step – calling the plugin (by including jQuery and the plugin files in the document if that hasn't been done previously) on the audio element:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token attr-name">preload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>audio</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jquery.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'audio'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">audioPlayer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <p>The plugin has some optional parameters. The most important one is called <code class="language-text">classPrefix</code>. The passed value becomes a class name for the parent element and class name prefix for child elements. Other options may only be advantageous for languages other than English. Example with default values:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'audio'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">audioPlayer</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">classPrefix</span><span class="token operator">:</span> <span class="token string">'audioplayer'</span><span class="token punctuation">,</span> <span class="token literal-property property">strPlay</span><span class="token operator">:</span> <span class="token string">'Play'</span><span class="token punctuation">,</span> <span class="token literal-property property">strPause</span><span class="token operator">:</span> <span class="token string">'Pause'</span><span class="token punctuation">,</span> <span class="token literal-property property">strVolume</span><span class="token operator">:</span> <span class="token string">'Volume'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="html" style="position:relative;"><a href="#html" aria-label="html permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML</h2> <p>As I mentioned in the very first paragraph, when the plugin is called, the <code class="language-text">&lt;audio></code> element is replaced then with some HTML:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer audioplayer-stopped<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token attr-name">preload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>audio</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-playpause<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Play<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Play<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-time audioplayer-time-current<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>00:00<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-bar-loaded<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-bar-played<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-time audioplayer-time-duration<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity named-entity" title="&hellip;">&amp;hellip;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-volume<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-volume-button<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Volume<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Volume<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-volume-adjust<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>There are some class names, which are assigned to the <strong>parent</strong> element when:</p> <ul> <li><em>audioplayer-playing</em> – audio is being played:</li> </ul> <p><code class="language-text">&lt;div class="audioplayer audioplayer-playing"></code></p> <ul> <li><em>audioplayer-stopped</em> – audio is being stopped:</li> </ul> <p><code class="language-text">&lt;div class="audioplayer audioplayer-stopped"></code></p> <ul> <li><em>audioplayer-muted</em> – volume is muted:</li> </ul> <p><code class="language-text">&lt;div class="audioplayer audioplayer-muted"></code></p> <ul> <li><em>audioplayer-novolume</em> – no volume adjustment is available:</li> </ul> <p><code class="language-text">&lt;div class="audioplayer audioplayer-novolume"></code></p> <p><strong>Important:</strong> when the audio element is not supported or none of the given audio files are compatible with the browser, the player switches to <em>Mini</em> mode, which basically reduces the player to <em>Play/Pause</em> button (because "embed" element is limited in terms of manipulation):</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer audioplayer-stopped audioplayer-mini<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>embed</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio.wav<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">volume</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">autostart</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">loop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audioplayer-playpause<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Play<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Play<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>Reminder: using <code class="language-text">classPrefix</code> options on the initiation of the plugin you can replace every <code class="language-text">audioplayer</code> occurrence with your own value in the HTML and so to have different looks of multiple players on the same website.</p> <p>Now, everything depends on how you will style the player using CSS. I will skip this part here and go directly to demo, but you can read <a href="http://tympanus.net/codrops/2012/12/04/responsive-touch-friendly-audio-player" target="_blank" rel="nofollow noopener noreferrer">my article on Codrops</a> about the styling anytime.</p> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p>As mentioned hundreds of times before, my player is responsive and isn't even dependent on Media Queries. I have a made a scheme which explains the layout:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/audio-player-responsive-and-touch-friendly-2.jpg?align=full" data-alt="Audio Player: Responsive and Touch-Friendly" data-align="full"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 50.2463054187192%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3lhQf//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAEQEQAxcf/aAAgBAQABPyG4OuC6X//aAAwDAQACAAMAAAAQc8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAADAAIDAAAAAAAAAAAAAAAAAREQcSExYf/aAAgBAQABPxBrWk3dUj0LHYi6Fwj/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/835a85b02681d95fe54be763af85ba63/3466e/audio-player-responsive-and-touch-friendly-2.webp 203w, /static/835a85b02681d95fe54be763af85ba63/d7d9a/audio-player-responsive-and-touch-friendly-2.webp 405w, /static/835a85b02681d95fe54be763af85ba63/7df04/audio-player-responsive-and-touch-friendly-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/835a85b02681d95fe54be763af85ba63/37414/audio-player-responsive-and-touch-friendly-2.jpg 203w, /static/835a85b02681d95fe54be763af85ba63/32fb1/audio-player-responsive-and-touch-friendly-2.jpg 405w, /static/835a85b02681d95fe54be763af85ba63/a6335/audio-player-responsive-and-touch-friendly-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/835a85b02681d95fe54be763af85ba63/a6335/audio-player-responsive-and-touch-friendly-2.jpg" alt="Audio Player: Responsive and Touch-Friendly" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>The parent element <code class="language-text">.audioplayer</code> is <code class="language-text">position: relative;</code> whereas the child elements <code class="language-text">.audioplayer-*</code> are <code class="language-text">position: absolute;</code>.</p> <p><a href="https://v3.osvaldas.info/examples/audio-player-responsive-and-touch-friendly" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <p>Grab the plugin: <a href="http://osvaldas.info/examples/audio-player-responsive-and-touch-friendly/audioplayer.js" target="_blank" rel="nofollow noopener noreferrer">audioplayer.js</a> (uncompressed; 8KB) or <a href="http://osvaldas.info/examples/audio-player-responsive-and-touch-friendly/audioplayer.min.js" target="_blank" rel="nofollow noopener noreferrer">audioplayer.min.js</a> (minified; 4KB).</p> <h2 id="feedback" style="position:relative;"><a href="#feedback" aria-label="feedback permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Feedback</h2> <p>I tested the player and it works fine on the latest Safari, Chrome, Firefox, Opera both Mac and Windows versions. The player works well on Internet Explorer 9, 10 and gracefully degrades to mini mode on earlier versions. I have also been lucky with iOS 6, Windows Phone 7 and Android 4.2 default browsers. However, the earlier Android versions does not support "audio" nor "embed" elements, so the player won't work there at all.</p> <p>P.S. do not confuse the plugin with the media centre or so. It was not built to have playlists or display album covers.</p><![CDATA[Responsive jQuery Masonry]]>https://osvaldas.info/responsive-jquery-masonry-or-pinterest-style-layouthttps://osvaldas.info/responsive-jquery-masonry-or-pinterest-style-layoutTue, 18 Sep 2012 00:00:00 GMT<p>…or Pinterest-style layout. <a href="http://masonry.desandro.com" target="_blank" rel="nofollow noopener noreferrer">jQuery Masonry</a> is a jQuery-based plugin for a dynamic grid layout built by David DeSandro. What it does is <em>“arranges elements vertically, positioning each element in the next open spot in the grid”</em> and what you get is <em>“minimized vertical gaps between elements of varying height”</em>. Let’s dive into that.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-jquery-masonry-1.jpg?align=full" data-alt="Responsive jQuery Masonry" data-align="full"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 50.73891625615764%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABssyYJ4gT/8QAGhAAAgIDAAAAAAAAAAAAAAAAAQIAEQMQEv/aAAgBAQABBQJAvDqtZxTyzr//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAABAwUBAAAAAAAAAAAAAAABABExAhIgISNB/9oACAEBAAY/AgLKSWQ5iQobXmH/xAAaEAACAwEBAAAAAAAAAAAAAAAAAREhMVFh/9oACAEBAAE/IXELveiUSXwERpKKEvp6M3T/2gAMAwEAAgADAAAAEBvP/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/ENiv/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQIBAT8Qp//EABsQAAIDAQEBAAAAAAAAAAAAAAERACExYVFx/9oACAEBAAE/ECOQDIC7cPlnsgs2HigiNsQhFU3mwZVfYzYiT2f/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/297d7c52e3a3cd3bf8067a5141142f26/3466e/responsive-jquery-masonry-1.webp 203w, /static/297d7c52e3a3cd3bf8067a5141142f26/d7d9a/responsive-jquery-masonry-1.webp 405w, /static/297d7c52e3a3cd3bf8067a5141142f26/7df04/responsive-jquery-masonry-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/297d7c52e3a3cd3bf8067a5141142f26/37414/responsive-jquery-masonry-1.jpg 203w, /static/297d7c52e3a3cd3bf8067a5141142f26/32fb1/responsive-jquery-masonry-1.jpg 405w, /static/297d7c52e3a3cd3bf8067a5141142f26/a6335/responsive-jquery-masonry-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/297d7c52e3a3cd3bf8067a5141142f26/a6335/responsive-jquery-masonry-1.jpg" alt="Responsive jQuery Masonry" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p><del>Sheepy Me</del> is a website where I constantly combine time-tested techniques and new trends. As for the blog where pictures act big in both ways, Pinterest inspired layout (a trend, popularized by Pinterest) probably was the best choice for listing items attractively. Although, JavaScript-based layout is not a good practice and should be avoided while CSS is capable to do this instead. This time, so far, it's not. Will take a look closer at this later in the post anyway.</p> <p>jQuery Masonry is not the only plugin and method for achieving the same effect, but it was the one actually working as well as working in the way I prefer. In responsive way. We're at the nice times when not thinking responsively is a bad habit and non-responsive techniques are considered as, to put it mildly, poor ones. jQuery Masonry is responsive-friendly, but that's not enough and some extra work is necessary in order to make it all meaningful.</p> <h2 id="the-grid" style="position:relative;"><a href="#the-grid" aria-label="the grid permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The Grid</h2> <p>Three-column grid is when screen width is higher than 640 pixels. Two columns – when screen width is equal to or less than 640 pixels, but is more than 320 pixels. And one column – when screen width is equal to or less than 320 pixels. The scheme may be easily adjusted to any other manner.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/responsive-jquery-masonry-2.jpg?align=full" data-alt="Responsive jQuery Masonry" data-align="full"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 63.05418719211823%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDAf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFibpMwP//EABoQAAEFAQAAAAAAAAAAAAAAAAEAAhITIRH/2gAIAQEAAQUCrCiE5vDap647/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAwADAAAAAAAAAAAAAAAAAAEQETFB/9oACAEBAAY/AuimhLE//8QAGRABAQEBAQEAAAAAAAAAAAAAAQARITFh/9oACAEBAAE/IbGy7zIw96bK3Q5H0ASs/9oADAMBAAIAAwAAABDjz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAQEBAAAAAAAAAAAAAAEAESExYdH/2gAIAQEAAT8Q9+g1CPahFNzKqFh6CQJBxj9jZmm75dQ1Zwrs/9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/0886b4ac7d6e19c3f24d7d928f472a43/3466e/responsive-jquery-masonry-2.webp 203w, /static/0886b4ac7d6e19c3f24d7d928f472a43/d7d9a/responsive-jquery-masonry-2.webp 405w, /static/0886b4ac7d6e19c3f24d7d928f472a43/7df04/responsive-jquery-masonry-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/0886b4ac7d6e19c3f24d7d928f472a43/37414/responsive-jquery-masonry-2.jpg 203w, /static/0886b4ac7d6e19c3f24d7d928f472a43/32fb1/responsive-jquery-masonry-2.jpg 405w, /static/0886b4ac7d6e19c3f24d7d928f472a43/a6335/responsive-jquery-masonry-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/0886b4ac7d6e19c3f24d7d928f472a43/a6335/responsive-jquery-masonry-2.jpg" alt="Responsive jQuery Masonry" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="setting-it-up" style="position:relative;"><a href="#setting-it-up" aria-label="setting it up permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Setting It Up</h2> <p>Apart from the obvious <em>include-jQuery-first</em>, let's see how actual things look like before bringing them to the world of responsiveness.</p> <p><strong>HTML</strong> structure is the simplest thing here. The further content goes into <code class="language-text">.item</code> elements. Thinking of better semantics, it would be nicer to go with unordered list. At Sheepy Me I used <code class="language-text">&lt;ul></code>'s for comments, date and tags to present into <code class="language-text">.item</code>'s. To avoid any conflict and keep CSS clean, div's was the only choice.</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p><strong>CSS</strong> is a little bit more specific as it puts our layout to fluid mode. Instead of ritual <code class="language-text">px</code>, I use <code class="language-text">em</code> (based on browser's default size <code class="language-text">16px</code>, <code class="language-text">100%</code>) and ` to define sizes and so to achieve complete flexibility across the browsers, screen sizes and <a href="http://stackoverflow.com/a/657632" target="_blank" rel="nofollow noopener noreferrer">people habits</a>.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#wrapper</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> 60em<span class="token punctuation">;</span> <span class="token comment">/* 960 px */</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0 auto<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 103.125%<span class="token punctuation">;</span> <span class="token comment">/* 990px */</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> -1.562%<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> -1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token punctuation">}</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 30.303%<span class="token punctuation">;</span> <span class="token comment">/* 300px */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0 1.515% 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 15px 30px */</span> <span class="token punctuation">}</span></code></pre></div> <p>What's interesting, I did a small workaround to solve the problem of horizontal margins by setting the width of <code class="language-text">#list</code> to more than 100% and left margin to the negative value. Though jQuery Masonry manages horizontal spacing automatically, the workaround gracefully degrades when JavaScript is disabled in the browser.</p> <p>Finally some <strong>JavaScript</strong>. Only shameless initialization of the plugin for now.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#list'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">masonry</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">itemSelector</span><span class="token operator">:</span> <span class="token string">'.item'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="pouring-in-responsiveness" style="position:relative;"><a href="#pouring-in-responsiveness" aria-label="pouring in responsiveness permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pouring In Responsiveness</h2> <p>Now that we have a working formation, let's teach it to adapt to savage environments: small and constantly changing screen sizes as well as zooming without remorse. However, the zoom is not handled well enough due to the absence of bulletproof active zooming detection.</p> <p>HTML stays the same, but CSS code gets a makeup – media queries. The first of them transforms the grid to two-column one, whilst the second to the simple one-column layout.</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#wrapper</span> <span class="token punctuation">{</span> <span class="token property">max-width</span><span class="token punctuation">:</span> 60em<span class="token punctuation">;</span> <span class="token comment">/* 960 px */</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0 auto<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 103.125%<span class="token punctuation">;</span> <span class="token comment">/* 990px */</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> -1.562%<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> -1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token punctuation">}</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 30.303%<span class="token punctuation">;</span> <span class="token comment">/* 300px */</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0 1.515% 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 15px 30px */</span> <span class="token punctuation">}</span> @media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 40em<span class="token punctuation">)</span> <span class="token comment">/* 640px */</span> <span class="token punctuation">{</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 46.876%<span class="token punctuation">;</span> <span class="token comment">/* 305px */</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 0.938em<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> @media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 20em<span class="token punctuation">)</span> <span class="token comment">/* 640px */</span> <span class="token punctuation">{</span> <span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>What's for JavaScript, function <code class="language-text">setColumns();</code> initially counts the number of columns accordingly to the screen width. <code class="language-text">$(window).resize(setColumns);</code> does the same, but when window is resized. jQuery Masonry, luckily, has an option named <code class="language-text">columnWidth</code>. Putting under (for value) a function actually solves the problem.</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> columns <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token function-variable function">setColumns</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> columns <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">640</span> <span class="token operator">?</span> <span class="token number">3</span> <span class="token operator">:</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">320</span> <span class="token operator">?</span> <span class="token number">2</span> <span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">setColumns</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">resize</span><span class="token punctuation">(</span>setColumns<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#list'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">masonry</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">itemSelector</span><span class="token operator">:</span> <span class="token string">'.item'</span><span class="token punctuation">,</span> <span class="token function-variable function">columnWidth</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">containerWidth</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> containerWidth <span class="token operator">/</span> columns<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>That's it! <del>Sheepy Me</del> is a real life example of the whole experience. Besides that, I made a separate demo which consists only of the relevant code and helps to focus on the technique itself.</p> <h3 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h3> <p><a href="https://v3.osvaldas.info/examples/responsive-jquery-masonry-or-pinterest-style-layout" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <h2 id="css-only-solution" style="position:relative;"><a href="#css-only-solution" aria-label="css only solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS-only solution?</h2> <p>Kushagra Agarwal <a href="http://cssdeck.com/labs/css-only-pinterest-style-columns-layout" target="_blank" rel="nofollow noopener noreferrer">suggests</a> to do without JavaScript and use CSS's <code class="language-text">column-*</code> properties instead. So how CSS code would change? <code class="language-text">#wrapper</code> remains the same, but the rest:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> -1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token property">-webkit-column-count</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span> <span class="token property">-webkit-column-gap</span><span class="token punctuation">:</span> 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token property">-webkit-column-fill</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">-moz-column-count</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span> <span class="token property">-moz-column-gap</span><span class="token punctuation">:</span> 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token property">-moz-column-fill</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">column-count</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span> <span class="token property">column-gap</span><span class="token punctuation">:</span> 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 30px */</span> <span class="token property">column-fill</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 1.875em<span class="token punctuation">;</span> <span class="token comment">/* 15px 30px */</span> <span class="token property">-webkit-column-break-inside</span><span class="token punctuation">:</span> avoid<span class="token punctuation">;</span> <span class="token property">-moz-column-break-inside</span><span class="token punctuation">:</span> avoid<span class="token punctuation">;</span> <span class="token property">column-break-inside</span><span class="token punctuation">:</span> avoid<span class="token punctuation">;</span> <span class="token punctuation">}</span> @media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 40em<span class="token punctuation">)</span> <span class="token comment">/* 640px */</span> <span class="token punctuation">{</span> <span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">-webkit-column-count</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token property">-webkit-column-gap</span><span class="token punctuation">:</span> 0.938em<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token property">-moz-column-count</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token property">-moz-column-gap</span><span class="token punctuation">:</span> 0.938em<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token property">column-count</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token property">column-gap</span><span class="token punctuation">:</span> 0.938em<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token punctuation">}</span> <span class="token selector">.item</span> <span class="token punctuation">{</span> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 0.938em<span class="token punctuation">;</span> <span class="token comment">/* 15px */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> @media only screen and <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 20em<span class="token punctuation">)</span> <span class="token comment">/* 320px */</span> <span class="token punctuation">{</span> <span class="token selector">#list</span> <span class="token punctuation">{</span> <span class="token property">-webkit-column-count</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">-moz-column-count</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">column-count</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <h3 id="demo-1" style="position:relative;"><a href="#demo-1" aria-label="demo 1 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h3> <p><a href="https://v3.osvaldas.info/examples/responsive-pinterest-style-layout-with-css" target="_blank" rel="nofollow noopener noreferrer"><strong>See the demo</strong></a>.</p> <p>How lovely is that, even with those vendor prefixes. It would absolutely be the best technique, if it hadn't the following limitations:</p> <ol> <li>Not supported by IE9 and below;</li> <li>Different rendering among supported browsers. Safari and Chrome does in one way, whilst Firefox and Opera in another. And both manners doesn't seem to be logical enough.</li> </ol> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p><code class="language-text">column-*</code> isn't fully standardized yet across web browsers. jQuery Masonry is a JavaScript thing. Two evils. You can stay with just one of them. You can combine them, by attaching jQuery Masonry only to <em>IE9-and-below</em> or <em>JavaScript-less</em> users and applying CSS columns for the rest. Whichever your decision is, just make sure it's best for your users.</p><![CDATA[A Book Apart: Design Is A Job]]>https://osvaldas.info/a-book-apart-design-is-a-jobhttps://osvaldas.info/a-book-apart-design-is-a-jobMon, 03 Sep 2012 00:00:00 GMT<p>The book <a href="http://www.abookapart.com/products/design-is-a-job" target="_blank" rel="nofollow noopener noreferrer">Design Is A Job</a> is not the first book from that wonderful A Book Apart collection I have read. But it's the book from the whole series that I want to talk about first, because it analyzes a different aspect of design as a profession, as a daily bread. Your design is useless if you are unable to sell it, if you can't convince people that your solutions are in the right place and at the right time. So finally there comes <a href="https://twitter.com/Mike_FTW" target="_blank" rel="nofollow noopener noreferrer">Mike Monteiro</a> from Mule Design to tell you how to do that and shares his best from 11 years experience.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/a-book-apart-design-is-a-job-1.jpg" data-alt="A Book Apart: Design Is A Job"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.004926108374384%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBP/EABQBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAdMgdwj/AP/EABkQAAIDAQAAAAAAAAAAAAAAAAERAAIDIf/aAAgBAQABBQLQOCnA1//EABcRAAMBAAAAAAAAAAAAAAAAAAEQERL/2gAIAQMBAT8B0ZF//8QAFxEAAwEAAAAAAAAAAAAAAAAAARAREv/aAAgBAgEBPwHItX//xAAYEAEAAwEAAAAAAAAAAAAAAAAAASExgf/aAAgBAQAGPwJs8W//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhYTFBUf/aAAgBAQABPyHBSx7viEWRU0m4fZ//2gAMAwEAAgADAAAAEAvv/8QAFhEAAwAAAAAAAAAAAAAAAAAAARBh/9oACAEDAQE/EBAX/8QAFhEAAwAAAAAAAAAAAAAAAAAAARBh/9oACAECAQE/EDQX/8QAGxABAAICAwAAAAAAAAAAAAAAAQARIWFBceH/2gAIAQEAAT8QdHBhek7CABu7iQuDhpV7rif/2Q==&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/d2587cd9eaf486d742b6b922e7176b94/3466e/a-book-apart-design-is-a-job-1.webp 203w, /static/d2587cd9eaf486d742b6b922e7176b94/d7d9a/a-book-apart-design-is-a-job-1.webp 405w, /static/d2587cd9eaf486d742b6b922e7176b94/7df04/a-book-apart-design-is-a-job-1.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/d2587cd9eaf486d742b6b922e7176b94/37414/a-book-apart-design-is-a-job-1.jpg 203w, /static/d2587cd9eaf486d742b6b922e7176b94/32fb1/a-book-apart-design-is-a-job-1.jpg 405w, /static/d2587cd9eaf486d742b6b922e7176b94/a6335/a-book-apart-design-is-a-job-1.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/d2587cd9eaf486d742b6b922e7176b94/a6335/a-book-apart-design-is-a-job-1.jpg" alt="A Book Apart: Design Is A Job" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>First of, this book was not written for people who expected 137 pages of wish-wash how clients are bad and how they want to suck you till the last drop of blood. Vice versa, the author would repeat himself a few times that <em>"there are no bad clients"</em> and you, you are the one and only blacksmith of your own fortune. I was lucky enough to notice <a href="https://twitter.com/irishstu/status/240923833354297344" target="_blank" rel="nofollow noopener noreferrer">one</a> of 400 million made-per-day-tweets (2012 statistics ) with this perfect statement which perfectly closes this completely vacuous topic: <em>"a lot of '<a href="http://clientsfromhell.net" target="_blank" rel="nofollow noopener noreferrer">clients from hell</a>' are 'designers who fail to communicate'"</em>.</p> <p>Reading the very first paragraphs may make a false impression. Personally, I found the 1st chapter a little bit prosaic. Luckily, it was just the first one and then things got far more entertaining. I think the author would agree that it wasn't very exciting, but it was necessary and a correct way to start this victorious <em>bénéfice</em>. While the foreword writer <a href="https://en.twitter.com/espiekermann" target="_blank" rel="nofollow noopener noreferrer">Erik Spiekermann</a> describes the author as <em>"a living bullshit detector"</em>, I'd add that Mike should turn his studio into a mediation-of-designer-to-client agency which, I have no doubt, would be leading agency in the field. Probably because Mike did not completely move away from making things and did not dedicate himself to clients. He stayed somewhere in the middle, successfully balancing between two sides.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/a-book-apart-design-is-a-job-2.jpg?align=overflow" data-alt="A Book Apart: Design Is A Job" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.591133004926114%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAgX/xAAWAQEBAQAAAAAAAAAAAAAAAAACAAH/2gAMAwEAAhADEAAAAeunLMpyYD//xAAbEAACAgMBAAAAAAAAAAAAAAAAAgEDEBESE//aAAgBAQABBQKC27g91kVpbFmlb//EABURAQEAAAAAAAAAAAAAAAAAAAAi/9oACAEDAQE/AUv/xAAWEQEBAQAAAAAAAAAAAAAAAAAAASL/2gAIAQIBAT8BrT//xAAbEAACAQUAAAAAAAAAAAAAAAAAAQIQITFBYf/aAAgBAQAGPwIwbI9pZI//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhQXGB/9oACAEBAAE/IWBAO0DW08gAeqjkrUNExyf/2gAMAwEAAgADAAAAEBQf/8QAGBEBAQADAAAAAAAAAAAAAAAAAQARIXH/2gAIAQMBAT8QMDu5b//EABcRAQEBAQAAAAAAAAAAAAAAAAEAIXH/2gAIAQIBAT8QCmXZf//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExQXGB/9oACAEBAAE/ECUANy7dB9gQFeZRDaAujRcVqDiqxXkXtgNZT//Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/cd9cdfe4b7a53eb0b56877885096be3d/3466e/a-book-apart-design-is-a-job-2.webp 203w, /static/cd9cdfe4b7a53eb0b56877885096be3d/d7d9a/a-book-apart-design-is-a-job-2.webp 405w, /static/cd9cdfe4b7a53eb0b56877885096be3d/7df04/a-book-apart-design-is-a-job-2.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/cd9cdfe4b7a53eb0b56877885096be3d/37414/a-book-apart-design-is-a-job-2.jpg 203w, /static/cd9cdfe4b7a53eb0b56877885096be3d/32fb1/a-book-apart-design-is-a-job-2.jpg 405w, /static/cd9cdfe4b7a53eb0b56877885096be3d/a6335/a-book-apart-design-is-a-job-2.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/cd9cdfe4b7a53eb0b56877885096be3d/a6335/a-book-apart-design-is-a-job-2.jpg" alt="A Book Apart: Design Is A Job" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p>Analyzing clients takes the lion's share of the book. From the very first handshake or phone call, to finally paying for work. <a href="http://vimeo.com/22053820" target="_blank" rel="nofollow noopener noreferrer">Mike does not wrap the words in wool</a>. He writes in style. Here's a classic situation how to smell the ruse:</p> <blockquote> <p>You know that douchebag at the bar who walks up to your friends and says "You know, I usually date models...". Yeah, that guy. The client services version of that is "You know, we've got some really big name agencies who'd love to get this job". Great, go call them. Don't work for someone who tries to make you feel they're lowering themselves to work with you, even as negotiation tactic. Good work comes from mutual respect.</p> </blockquote> <p>Having said that, Mike also perceives how important self-treatment is. If you do not respect yourself, no one else will. Designers, first of all, are thinkers, innovators and so problem solvers. Technical work is the last thing you need to do.</p> <blockquote> <p>Beware of clients who have waited to call you until they have a perfect diagram of what they need and want you to color it in. If they're not coming to you for strategy and problem-solving, they're not coming to you for design, they're coming to you for production. And if you take on production work, you don't get to call yourself a designer.</p> </blockquote> <p>I would exclude the 7<sup>th</sup> chapter as the most important for me personally. I will read it every time (until I finally memorize it word by word) before presenting my work to clients. Presenting your work in a room could be a crucial moment. Coincidence or not, but I had been watching <a href="http://www.amctv.com/shows/mad-men" target="_blank" rel="nofollow noopener noreferrer">Mad Men</a> series and reading this book at the same time. At some point, it was like watching this book based movie. Double effect.</p> <blockquote> <p>Stop trying to get your clients to "understand design" and instead show them that you understand what they hired you to do. Explain how choices you've made lead to a successful project.</p> </blockquote> <p>Something that is obvious but not always visible – I remember myself being this immature:</p> <blockquote> <p>Don't waste client's time walking them through what they can already see. Your job is to explain how what they're looking at is the best way to achieve their goals.</p> </blockquote> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; " data-url="../../images/blog/a-book-apart-design-is-a-job-3.jpg?align=overflow" data-alt="A Book Apart: Design Is A Job" data-align="overflow"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.591133004926114%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB6slDBRhL/8QAGxAAAgEFAAAAAAAAAAAAAAAAAAEDAhAREiH/2gAIAQEAAQUC7pHE1WJmbf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAQABBQAAAAAAAAAAAAAAAAECABESIEH/2gAIAQEABj8CcS7wpnLX/8QAGhABAQEAAwEAAAAAAAAAAAAAAQARITFBwf/aAAgBAQABPyFwgeo5aai9H1smZWS83//aAAwDAQACAAMAAAAQZ+//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAn/8QAFhEBAQEAAAAAAAAAAAAAAAAAERAh/9oACAECAQE/EHCf/8QAGxABAAMAAwEAAAAAAAAAAAAAAQARMSFBUXH/2gAIAQEAAT8QRuJgF+xp2KerfaNXrwjZt2cgvMJqO9J//9k=&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/72f15e92b0ef87ec1f8b69d10fe76a86/3466e/a-book-apart-design-is-a-job-3.webp 203w, /static/72f15e92b0ef87ec1f8b69d10fe76a86/d7d9a/a-book-apart-design-is-a-job-3.webp 405w, /static/72f15e92b0ef87ec1f8b69d10fe76a86/7df04/a-book-apart-design-is-a-job-3.webp 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/webp"> <source srcset="/static/72f15e92b0ef87ec1f8b69d10fe76a86/37414/a-book-apart-design-is-a-job-3.jpg 203w, /static/72f15e92b0ef87ec1f8b69d10fe76a86/32fb1/a-book-apart-design-is-a-job-3.jpg 405w, /static/72f15e92b0ef87ec1f8b69d10fe76a86/a6335/a-book-apart-design-is-a-job-3.jpg 760w" sizes="(max-width: 760px) 100vw, 760px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/72f15e92b0ef87ec1f8b69d10fe76a86/a6335/a-book-apart-design-is-a-job-3.jpg" alt="A Book Apart: Design Is A Job" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <p><em>Design Is A Job</em> could be useful at every aspect of your professional life. Well, obviously, except pushing pixels and writing a markup. It's perfect if you are a freelancer or own an agency. It's still great if you work at agency. It's indispensable if you build websites. It's still relevant if you make logos or do other graphic work. <strong>Recommended</strong>.</p><![CDATA[Gestures-Sensitive Slideshow: Responsive and Touch-Friendly]]>https://osvaldas.info/gestures-sensitive-slideshow-responsive-and-touch-friendlyhttps://osvaldas.info/gestures-sensitive-slideshow-responsive-and-touch-friendlyTue, 22 May 2012 00:00:00 GMT<p>I'm not quite sure, but it was probably 2006 (correct me if I'm wrong) when with the birth of jQuery the very first non-Flash slideshows/sliders appeared on Web. Before that time, writing JavaScript was a horrible pain and therefore websites were boring, Internet wasn't that attractive as it is nowadays. Because nobody wanted to spend a week writing a thousand-lines code just to have something that does not work properly on any browser! Luckily, things have changed: JavaScript had become practical and that had heavily influenced the whole craft. Now things have changed even more: you can't ignore mobile &#x26; tablet devices, you must have your content <a href="http://dbushell.com/2012/03/03/forget-about-browser-support" target="_blank" rel="nofollow noopener noreferrer">accessible</a> on any modern device.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; " data-url="../../images/blog/gestures-sensitive-slideshow-responsive-and-touch-friendly.jpg?align=full" data-alt="Gestures-Sensitive Slideshow &#x2013; Responsive and Touch-Friendly" data-align="full"> <span class="gatsby-resp-image-background-image" style="padding-bottom: 53.69458128078818%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAECAwT/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAQL/2gAMAwEAAhADEAAAAc8mEUi4mv/EABgQAAMBAQAAAAAAAAAAAAAAAAABERAi/9oACAEBAAEFAr27CCz/xAAYEQADAQEAAAAAAAAAAAAAAAAAARECMf/aAAgBAwEBPwF5d6U//8QAFxEBAQEBAAAAAAAAAAAAAAAAEQABAv/aAAgBAgEBPwHOsAi//8QAGBAAAgMAAAAAAAAAAAAAAAAAABEgIjH/2gAIAQEABj8CRXY//8QAGhABAQEAAwEAAAAAAAAAAAAAAREAECExgf/aAAgBAQABPyHxbCXFL4XFnZkPphOP/9oADAMBAAIAAwAAABAfL//EABcRAAMBAAAAAAAAAAAAAAAAAAEQETH/2gAIAQMBAT8QIrCf/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERQf/aAAgBAgEBPxCLoVf/xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhQVGh/9oACAEBAAE/EFl4Wvb5OB3XH5Axi9I3YKekBYVAyf/Z&apos;); background-size: cover; display: block;"/> <picture> <source srcset="/static/20325c4173710fec192f9b3986cf06ec/3466e/gestures-sensitive-slideshow-responsive-and-touch-friendly.webp 203w, /static/20325c4173710fec192f9b3986cf06ec/d7d9a/gestures-sensitive-slideshow-responsive-and-touch-friendly.webp 405w, /static/20325c4173710fec192f9b3986cf06ec/0c8fb/gestures-sensitive-slideshow-responsive-and-touch-friendly.webp 640w" sizes="(max-width: 640px) 100vw, 640px" type="image/webp"> <source srcset="/static/20325c4173710fec192f9b3986cf06ec/37414/gestures-sensitive-slideshow-responsive-and-touch-friendly.jpg 203w, /static/20325c4173710fec192f9b3986cf06ec/32fb1/gestures-sensitive-slideshow-responsive-and-touch-friendly.jpg 405w, /static/20325c4173710fec192f9b3986cf06ec/56d4e/gestures-sensitive-slideshow-responsive-and-touch-friendly.jpg 640w" sizes="(max-width: 640px) 100vw, 640px" type="image/jpeg"> <img class="gatsby-resp-image-image" src="/static/20325c4173710fec192f9b3986cf06ec/56d4e/gestures-sensitive-slideshow-responsive-and-touch-friendly.jpg" alt="Gestures-Sensitive Slideshow &#x2013; Responsive and Touch-Friendly" title="" loading="lazy" decoding="async" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"> </img></source></source></picture> </span></p> <h2 id="the-problem" style="position:relative;"><a href="#the-problem" aria-label="the problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The Problem</h2> <p>While working on a client project, I wanted to present interior portfolio in appealing and more appropriate way than what usually resides under the <em>slideshow</em> term. Not to mention accessibility on touch devices. So I thought: after all, moving mouse or sliding finger on screen is much easier than clicking or tapping arrows for dozens of times, right? Voila!</p> <h2 id="the-solution" style="position:relative;"><a href="#the-solution" aria-label="the solution permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The Solution</h2> <p>The technique is based on some lines of HTML, CSS, JavaScript and (whole) jQuery framework. Unlike <a href="/elegant-css-and-jquery-tooltip-responsive-mobile-friendly">responsive and touch-friendly tooltip</a>, I made it plugin-like so that you can call plenty of the slideshow instances from wherever you want.</p> <p>Notice that the slideshow is returning what means anchors do not perform their primary function on touch devices. Consequently, this type of slideshow may not fit in every situation. Anyway, if you have a specific purpose, as it is in my case, the technique should serve well.</p> <h2 id="html" style="position:relative;"><a href="#html" aria-label="html permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HTML</h2> <p>The structure couldn't be simpler: container and unordered list. What about content? It's totally up to your imagination: it could be anything from pure images to tables. An example with images:</p> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slideshow<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>4.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="css" style="position:relative;"><a href="#css" aria-label="css permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS</h2> <p>I think you have just felt what the CSS code is like only by looking at the HTML above, but just in case, here's what to start with:</p> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#slideshow</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 500px<span class="token punctuation">;</span> <span class="token comment">/* slideshow height */</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token comment">/* or absolute, but not static */</span> <span class="token punctuation">}</span> <span class="token selector">#slideshow ul</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 9999%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">#slideshow li</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0 20px<span class="token punctuation">;</span> <span class="token comment">/* spacing between items */</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="javascript" style="position:relative;"><a href="#javascript" aria-label="javascript permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaScript</h2> <p>The plugin, actually, is very tiny, however, it has too many lines to paste them here. Paradox. So I made <strong><a href="https://v3.osvaldas.info/examples/gestures-sensitive-slideshow-responsive-and-touch-friendly/gestured-slideshow.js" target="_blank" rel="nofollow noopener noreferrer">gestured-slideshow.js</a></strong> available in the next tab of your browser.</p> <p>There's no surprise you can call slideshow instance(s) in this way:</p> <div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span> <span class="token string">'#slideshow'</span> <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">gesturedSlideshow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Captions, shadows, rounded borders, gradients, textures, animations and other loud phrases, for sure, are the way to style your next slideshow, but I decided to keep my demo calm and technique-focused.</p> <h2 id="demo" style="position:relative;"><a href="#demo" aria-label="demo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2> <p><a href="https://v3.osvaldas.info/examples/gestures-sensitive-slideshow-responsive-and-touch-friendly&q