Archiwum kategorii: CSS

When Design Becomes Part of the Code Workflow

Post pobrano z: When Design Becomes Part of the Code Workflow

I recently did an experiment where I created the same vector illustration in three different applications, exported the illustration as SVG in each application, then wrote a post comparing the exported code.

While I loved the banter and insights that came in the comments, I was surprised that the bulk of conversation was centered on the file size of the compiled SVG.

I wasn’t surprised because performance and SVG do not go hand-in-hand or that performance isn’t the sort of thing we generally care about in the front-end community. I was surprised because my personal takeaway from the experiment was a reminder that SVG code is code at the end of the day and that the way we create SVG in applications is now more a part of the front-end workflow than perhaps it has been in the past.

I still believe that is the key point from the post and wanted to write a follow-up that not only more clearly articulates it, but also details how we may need to change the way we think about design deliverables for projects that use SVG.

The gap between design and code is getting narrower

We already know this and have extolled the virtues of designers who know how to code. However, what the SVG experiment revealed to me is that those virtues are no longer so much an ideal as much as they are a growing necessity.

If a project calls for SVG and a designer has been tasked with creating illustrations and providing design assets for development, then the designer is no longer handing over a static file, but a snippet of code and, depending on the scope of the project, that code may very well be inlined or injected directly into the HTML document.

Sure, we can intervene and check the code that is provided. We may even run it through a tool like SVGOMG or have automated tasks that help clean and optimize the code before it gets served to production. That is all great, but does not change the fact that what we were delivered in the first place was a piece of code and that there is now an additional consideration in our workflow to code review a design asset.

That’s a significant change. It’s not a bad change or even true in all scenarios, but it is a significant one for no reason more than it requires a change in how we think about, request, and handle design deliverables on a project.

A new era of design etiquette is upon us

I was one of many, many fans of the Photoshop Etiquette site when I learned about it. It not only struck about a dozen nerves that rang true to my own experiences working with other designers on web projects, but forced me to re-examine and improve my own design practices for the benefit of working within teams. Tips like nicely organized layers with a consistent naming convention make a world of difference when a file is handed off from one person to another, much like nicely documented CSS that uses consistent naming conventions and is generous with comments.

SVG makes these tips much more about necessity than etiquette. Again, now that we have a design deliverable that becomes code, the decisions a designer makes—from configuring an artboard to how the layers are grouped and named—all influence how the SVG code is compiled and ultimately used in production.

Perhaps it’s time for an offshoot of Photoshop Etiquette that is more squarely focused on SVG design deliverables using illustration applications.

Applications are super smart, but still need human intervention

My favorite comment from the previous post was a manually coded rendition of the SVG illustration. The code was much cleaner and way more efficient than any of the versions generated by the applications being compared.

Whether or not it was the point of the comment, what I love most about it is how it proves we cannot always take what applications give us for granted. It’s freaking amazing that an application like Sketch can take a series of shapes I draw on a screen and turn them into valid and working code, but is it the best code for the situation? It could be. Then again, the commenter proved that it could be done better if the goal was a smaller file size and more readable code.

All three of the applications I tested are remarkably smart, incredibly useful, and have unique strengths that make each one a legitimate and indispensable tool in anyone’s web development arsenal. The point here is not to distrust them or stay away from using them.

The point is that they are only as smart as the people using them. If we give them bad shapes and disorganized layers, then we can likely expect less-than-optimal code in return. I would go so far as to say that my method for creating the illustration in the experiment likely influenced the final output in all three cases and may not have given the applications the best shot for generating stellar code.

Either way, it took a human reviewing that generated code and optimizing it by hand to make the point.

Wrapping Up

I want to give a big ol’ thanks to everyone who commented on the previous post. What started as a simple personal curiosity became a more nuanced experiment and I was stoked to see it spark healthy debate and insightful ideas. It was those comments and some ensuing offline conversations that made me think deeper about about the the hand-off between design and development which ultimately wound up being the key takeaway from the entire exercise.


When Design Becomes Part of the Code Workflow is a post from CSS-Tricks

JavaScript Scope and Closures

Post pobrano z: JavaScript Scope and Closures

Scopes and closures are important in JavaScript. But, they were confusing for me when I first started. Here’s an explanation of scopes and closures to help you understand what they are.

Let’s start with scopes.

Scope

A scope in JavaScript defines what variables you have access to. There are two kinds of scope – global scope and local scope.

Global scope

If a variable is declared outside all functions or curly braces ({}), it is said to be defined in the global scope.

This is true only with JavaScript in web browsers. You declare global variables in Node.js differently, but we won’t go into Node.js in this article.

const globalVariable = 'some value'

Once you’ve declared a global variable, you can use that variable anywhere in your code, even in functions.

const hello = 'Hello CSS-Tricks Reader!'

function sayHello () {
  console.log(hello)
}

console.log(hello) // 'Hello CSS-Tricks Reader!'
sayHello() // 'Hello CSS-Tricks Reader!'

Although you can declare variables in the global scope, it is advised not to. This is because there is a chance of naming collisions, where two or more variables are named the same. If you declared your variables with const or let, you would receive an error whenever you a name collision happens. This is undesirable.

// Don't do this!
let thing = 'something'
let thing = 'something else' // Error, thing has already been declared

If you declare your variables with var, your second variable overwrites the first one after it is declared. This also undesirable as you make your code hard to debug.

// Don't do this!
var thing = 'something'
var thing = 'something else' // perhaps somewhere totally different in your code
console.log(thing) // 'something else'

So, you should always declare local variables, not global variables.

Local Scope

Variables that are usable only in a specific part of your code are considered to be in a local scope. These variables are also called local variables.

In JavaScript, there are two kinds of local scope: function scope and block scope.

Let’s talk about function scopes first.

Function scope

When you declare a variable in a function, you can access this variable only within the function. You can’t get this variable once you get out of it.

In the example below, the variable hello is in the sayHello scope:

function sayHello () {
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello)
}

sayHello() // 'Hello CSS-Tricks Reader!'
console.log(hello) // Error, hello is not defined

Block scope

When you declare a variable with const or let within a curly brace ({}), you can access this variable only within that curly brace.

In the example below, you can see that hello is scoped to the curly brace:

{
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello) // 'Hello CSS-Tricks Reader!'
}

console.log(hello) // Error, hello is not defined

The block scope is a subset of a function scope since functions need to be declared with curly braces (unless you’re using arrow functions with an implicit return).

Function hoisting and scopes

Functions, when declared with a function declaration, are always hoisted to the top of the current scope. So, these two are equivalent:

// This is the same as the one below
sayHello()
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}

// This is the same as the code above
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}
sayHello()

When declared with a function expression, functions are not hoisted to the top of the current scope.

sayHello() // Error, sayHello is not defined
const sayHello = function () {
  console.log(aFunction)
}

Because of these two variations, function hoisting can potentially be confusing, and should not be used. Always declare your functions before you use them.

Functions do not have access to each other’s scopes

Functions do not have access to each other’s scopes when you define them separately, even though one function may be used in another.

In this example below, second does not have access to firstFunctionVariable.

function first () {
  const firstFunctionVariable = `I'm part of first`
}

function second () {
  first()
  console.log(firstFunctionVariable) // Error, firstFunctionVariable is not defined
}

Nested scopes

When a function is defined in another function, the inner function has access to the outer function’s variables. This behavior is called lexical scoping.

However, the outer function does not have access to the inner function’s variables.

function outerFunction () {
  const outer = `I'm the outer function!`

  function innerFunction() {
    const inner = `I'm the inner function!`
    console.log(outer) // I'm the outer function!
  }

  console.log(inner) // Error, inner is not defined
}

To visualize how scopes work, you can imagine one-way glass. You can see the outside, but people from the outside cannot see you.

Scopes in functions behave like a one-way-glass. You can see the outside, but people outside can’t see you

If you have scopes within scopes, visualize multiple layers of one-way glass.

Multiple layers of functions mean multiple layers of one-way glass

After understanding everything about scopes so far, you’re well primed to figure out what closures are.

Closures

Whenever you create a function within another function, you have created a closure. The inner function is the closure. This closure is usually returned so you can use the outer function’s variables at a later time.

function outerFunction () {
  const outer = `I see the outer variable!`

  function innerFunction() {
    console.log(outer)
  }

  return innerFunction
}

outerFunction()() // I see the outer variable!

Since the inner function is returned, you can also shorten the code a little by writing a return statement while declaring the function.

function outerFunction () {
  const outer = `I see the outer variable!`

  return function innerFunction() {
    console.log(outer)
  }
}

outerFunction()() // I see the outer variable!

Since closures have access to the variables in the outer function, they are usually used for two things:

  1. To control side effects
  2. To create private variables

Controlling side effects with closures

Side effects happen when you do something in aside from returning a value from a function. Many things can be side effects, like an Ajax request, a timeout or even a console.log statement:

function (x) {
  console.log('A console.log is a side effect!')
}

When you use closures to control side effects, you’re usually concerned with ones that can mess up your code flow like Ajax or timeouts.

Let’s go through this with an example to make things clearer.

Let’s say you want to make a cake for your friend’s birthday. This cake would take a second to make, so you wrote a function that logs made a cake after one second.

I’m using ES6 arrow functions here to make the example shorter, and easier to understand.

function makeCake() {
  setTimeout(_ => console.log(`Made a cake`, 1000)
  )
}

As you can see, this cake making function has a side effect: a timeout.

Let’s further say you want your friend to choose a flavor for the cake. To do so, you can write add a flavor to your makeCake function.

function makeCake(flavor) {
  setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
}

When you run the function, notice the cake gets made immediately after one second.

makeCake('banana')
// Made a banana cake!

The problem here is that you don’t want to make the cake immediately after knowing the flavor. You want to make it later when the time is right.

To solve this problem, you can write a prepareCake function that stores your flavor. Then, return the makeCake closure within prepareCake.

From this point on, you can call the returned function whenever you want to, and the cake will be made within a second.

function prepareCake (flavor) {
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

// And later in your code...
makeCakeLater()
// Made a banana cake!

That’s how closures are used to reduce side effects – you create a function that activates the inner closure at your whim.

Private variables with closures

As you know by now, variables created in a function cannot be accessed outside the function. Since they can’t be accessed, they are also called private variables.

However, sometimes you need to access such a private variable. You can do so with the help of closures.

function secret (secretCode) {
  return {
    saySecretCode () {
      console.log(secretCode)
    }
  }
}

const theSecret = secret('CSS Tricks is amazing')
theSecret.saySecretCode()
// 'CSS Tricks is amazing'

saySecretCode in this example above is the only function (a closure) that exposes the secretCode outside the original secret function. As such, it is also called a privileged function.

Debugging scopes with DevTools

Chrome and Firefox’s DevTools make it simple for you to debug variables you can access in the current scope. There are two ways to use this functionality.

The first way is to add the debugger keyword in your code. This causes JavaScript execution in browsers to pause so you can debug.

Here’s an example with the prepareCake:

function prepareCake (flavor) {
  // Adding debugger
  debugger
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

If you open your DevTools and navigate to the Sources tab in Chrome (or Debugger tab in Firefox), you would see the variables available to you.

Debugging prepareCake’s scope

You can also shift the debugger keyword into the closure. Notice how the scope variables changes this time:

function prepareCake (flavor) {
  return function () {
    // Adding debugger
    debugger
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')
Debugging the closure scope

The second way to use this debugging functionality is to add a breakpoint to your code directly in the sources (or debugger) tab by clicking on the line number.

Debugging scopes by adding breakpoints

Wrapping up

Scopes and closures aren’t incredibly hard to understand. They’re pretty simple once you know how to see them through a one-way glass.

When you declare a variable in a function, you can only access it in the function. These variables are said to be scoped to the function.

If you define any inner function within another function, this inner function is called a closure. It retains access to the variables created in the outer function.

Feel free to pop by and ask any questions you have. I’ll get back to you as soon as I can.

If you liked this article, you may also like other front-end-related articles I write on my blog and my newsletter. I also have a brand new (and free!) email course: JavaScript Roadmap.


JavaScript Scope and Closures is a post from CSS-Tricks

Managing CSS & JS in an HTTP/2 World

Post pobrano z: Managing CSS & JS in an HTTP/2 World

Trevor Davis on how we’ll link up CSS when we go all-in on HTTP/2:

This is the opposite of what we have done as best practice for years now. But in order to take advantage of multiplexing, it’s best to break up your CSS into smaller files so that only the necessary CSS is loaded on each page. An example page markup would look something like this:

<link href="stylesheets/modules/text-block/index.css" rel="stylesheet">
<div class="text-block">
  ...
</div>

<link href="stylesheets/modules/two-column-block/index.css" rel="stylesheet">
<div class="two-column-block">
  ...
</div>

This idea shares some DNA with Critical CSS. Loading CSS with <link> is blocking, so load as little of it as you can right away and load the rest of it as you need it. There is no penalty for loading the stylesheets individually because of HTTP/2 multiplexing, and loading them right before the HTML that uses them actually takes advantage of the blocking by not allowing the rendering of the HTML before the CSS for it is gotten. Plus you’ll be able to break cache on these smaller bits of CSS as needed, just bear in mind it might not compress as well.

The thing is… for any browser that doesn’t support HTTP/2 (e.g. IE 10, Opera mobile/mini, UC browser), while this technique will still work, it will be pretty bad for performance. This will be an easier call to make on projects that don’t need to support those browsers for whatever reason.

Direct Link to Article — Permalink


Managing CSS & JS in an HTTP/2 World is a post from CSS-Tricks

Form Validation with Web Audio

Post pobrano z: Form Validation with Web Audio

I’ve been thinking about sound on websites for a while now.

When we talk about using sound on websites, most of us grimace and think of the old days, when blaring background music played when the website loaded.

Today this isn’t and needn’t be a thing. We can get clever with sound. We have the Web Audio API now and it gives us a great deal of control over how we design sound to be used within our web applications.

In this article, we’ll experiment with just one simple example: a form.

What if when you were filling out a form it gave you auditory feedback as well as visual feedback. I can see your grimacing faces! But give me a moment.

We already have a lot of auditory feedback within the digital products we use. The keyboard on a phone produces a tapping sound. Even if you have „message received” sounds turned off, you’re more than likely able to hear your phone vibrate. My MacBook makes a sound when I restart it and so do games consoles. Auditory feedback is everywhere and pretty well integrated, to the point that we don’t really think about it. When was the last time you grimaced at the microwave when it pinged? I bet you’re glad you didn’t have to watch it to know when it was done.

As I’m writing this article my computer just pinged. One of my open tabs sent me a useful notification. My point being sound can be helpful. We may not all need to know with our ears whether we’ve filled a form incorrectly, there may be plenty of people out there that do find it beneficial.

So I’m going to try it!

Why now? We have the capabilities at our finger tips now. I already mentioned the Web Audio API, we can use this to create/load and play sounds. Add this to HTML form validating capabilities and we should be all set to go.

Let’s start with a small form.

Here’s a simple sign up form.

See the Pen Simple Form by Chris Coyier (@chriscoyier) on CodePen.

We can wire up a form like this with really robust validation.

With everything we learned from Chris Ferdinandi’s guide to form validation, here’s a version of that form with validation:

See the Pen Simple Form with Validation by Chris Coyier (@chriscoyier) on CodePen.

Getting The Sounds Ready

We don’t want awful obtrusive sounds, but we do want those sounds to represent success and failure. One simple way to do this would be to have a higher, brighter sounds which go up for success and lower, more distorted sounds that go down for failure. This still gives us very broad options to choose from but is a general sound design pattern.

With the Web Audio API, we can create sounds right in the browser. Here are examples of little functions that play positive and negative sounds:

See the Pen Created Sounds by Chris Coyier (@chriscoyier) on CodePen.

Those are examples of creating sound with the oscillator, which is kinda cool because it doesn’t require any web requests. You’re literally coding the sounds. It’s a bit like the SVG of the sound world. It can be fun, but it can be a lot of work and a lot of code.

While I was playing around with this idea, FaceBook released their SoundKit, which is:

To help designers explore how sound can impact their designs, Facebook Design created a collection of interaction sounds for prototypes.

Here’s an example of selecting a few sounds from that and playing them:

See the Pen Playing Sound Files by Chris Coyier (@chriscoyier) on CodePen.

Another way would be to fetch the sound file and use the audioBufferSourceNode. As we’re using small files there isn’t much overhead here, but, the demo above does fetch the file over the network everytime it is played. If we put the sound in a buffer, we wouldn’t have to do that.

Figuring Out When to Play the Sounds

This experiment of adding sounds to a form brings up a lot of questions around the UX of using sound within an interface.

So far, we have two sounds, a positive/success sound and a negative/fail sound. It makes sense that we’d play these sounds to alert the user of these scenarios. But when exactly?

Here’s some food for thought:

  • Do we play sound for everyone, or is it an opt-in scenario? opt-out? Are there APIs or media queries we can use to inform the default?
  • Do we play success and fail sounds upon form submission or is it at the individual input level? Or maybe even groups/fieldsets/pages?
  • If we’re playing sounds for each input, when do we do that? On blur?
  • Do we play sounds on every blur? Is there different logic for success and fail sounds, like only one fail sound until it’s fixed?

There aren’t any extremely established best practices for this stuff. The best we can do is make tasteful choices and do user research. Which is to say, the examples in this post are ideas, not gospel.

Demo

Here’s one!

View Demo

And here’s a video, with sound, of it working:

Voice

Greg Genovese has an article all about form validation and screen readers. „Readers” being relevant here, as that’s all about audio! There is a lot to be done with aria roles and moving focus and such so that errors are clear and it’s clear how to fix them.

The Web Audio API could play a role here as well, or more likely, the Web Speech API. Audio feedback for form validation need not be limited to screen reader software. It certainly would be interesting to experiment with reading out actual error messages, perhaps in conjunction with other sounds like we’ve experimented with here.

Thoughts

All of this is what I call Sound Design in Web Design. It’s not merely just playing music and sounds, it’s giving the sound scape thought and undertaking some planning and designing like you would with any other aspect of what you design and build.

There is loads more to be said on this topic and absolutely more ways in which you can use sound in your designs. Let’s talk about it!


Form Validation with Web Audio is a post from CSS-Tricks

Prefilling a Date Input

Post pobrano z: Prefilling a Date Input

HTML has a special input type for dates, like this: <input type="date">. In supporting browsers (pretty good), users will get UI for selecting a date. Super useful stuff, especially since it falls back to a usable text input. But how do you set it to a particular day?

To set a particular day, you’ll need to set the value to a YYYY-MM-DD format, like this:

<input type="date" value="1980-08-26">

Minor note: placeholder won’t do anything in a browser that supports date inputs. Date inputs can have min and max, so only a date between a particular range can be selected. Those take the same format. Just for fun we’ve used a step value here to make only Tuesday selectable:

<input type="date" min="2017-08-15" max="2018-08-26" step="7">

How about defaulting the input to the value of today? Unfortunately, there is no HTML-only solution for that, but it’s possible with JavaScript.

<input id="today" type="date">
let today = new Date().toISOString().substr(0, 10);
document.querySelector("#today").value = today;

// or...

document.querySelector("#today").valueAsDate = new Date();

It’s also possible to select a specific week or month. Prefilling those is like this:

<input type="week" value="2014-W02">

<input type="month" value="2018-08">

If you need both date and time, there is an input for that as well. Just for fun

<input type="datetime-local" value="2017-06-13T13:00">

Or just time! Here we’ll use step again just for fun to limit it to 15 minute increments:

<input type="time" value="13:00" step="900">

Live Demo

See the Pen Prefilling HTML date inputs by Chris Coyier (@chriscoyier) on CodePen.

Support

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
20 9 57 No 13 No

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11 10 No 4.4 59 55

Prefilling a Date Input is a post from CSS-Tricks

Strongly Held Opinions, Gone Away

Post pobrano z: Strongly Held Opinions, Gone Away

I received a really wonderful question from Bryan Braun the other day during a workshop I was giving at Sparkbox. He asked if, over the years, if there were opinions about web design and development I strongly held that I don’t anymore.

I really didn’t have a great answer at the time, even though surely if I could rewind my brain there would be some embarrassing ones in there.

At the risk of some heavy self-back-patting, this is exactly the reason I try and be pretty open-minded. If you aren’t, you end up eating crow. And for what? When you crap on an idea, you sound like a jerk at the time, and likely cause more harm than good. If you end up right, you were still a jerk. If you end up wrong, you were a jerk and a fool.

I like the sentiment the web is a big place. It’s a quick way of saying there are no hard and fast right answers in a playground this big with loose rules, diversity of everything, and economic overlords.

I don’t want to completely punt on this question though.

I’ve heard Trent Walton say a number of times that, despite him being all-in on Responsive Web Design now, at first it seemed like a very bad idea to him.

I remember feeling very late to the CSS preprocessing world, because I spent years rolling my eyes at it. I thought it the result of back end nerds sticking their noses into something and bringing programming somewhere that didn’t need it. Looking back, it was mostly me being afraid to learn the tools needed to bring it into a workflow.

It’s not to find industry-wide holy wars these days, where strongly held opinions duke it over time, and probably end up giving ground to each other in the end.

But what of those internal personal battles? I’d be very interested to hear people’s answers on this…

What strongly-help opinion did you used to have about web design and development, but not anymore?


Strongly Held Opinions, Gone Away is a post from CSS-Tricks

Double Opt-In Email Intros

Post pobrano z: Double Opt-In Email Intros

You know those those „introduction” emails? Someone thinks you should meet someone else, and emails happen about it. Or it’s you doing the introducing, either by request or because you think it’s a good idea. Cutting to the chase here, those emails could be done better. Eight years ago, Fred Wilson coined the term „double opt-in intro”.

This is how it can work.

You’re doing the vetting

Since you’re writing the emails here, it’s your reputation at stake here. If you do an introduction that is obnoxious for either side, they’ll remember. Make sure you’re introducing people that you really do think should know each other. Like a bizdev cupid.

You’re gonna do two (or three) times writing

The bad way to do an intro is to email both people at once. Even if this introduction has passed your vetting, you have no idea how it’s going to turn out. There is a decent chance either of them or both aren’t particularly interested in this, which makes you look like a dolt. It doesn’t respect either of their time, puts your reputation at risk, and immediately puts everyone into an awkward position (if they ignore it they look like an asshole).

Instead, you’re going to write two emails, one to each person you’re trying to introduce. And you’re not going to reveal who the other person is, except with non-identifying relevant details and your endorsement.

They do the opt-ing in

If either of the folks are interested in this introduction, they can email you back. Give them an easy out though, I’d say something like „if for any reason you aren’t into it, just tell me so or ignore this, I promise I understand”. If you don’t make it easy to blow you off, it’s your just transferring the awkward situation to yourself.

If either of them isn’t into it, it doesn’t matter. They don’t know who the other is and there is no awkwardness or burnt bridge.

If both are into it, great, now it’s time for the third email actually introducing them. Get out of the way quickly.

It’s about more than awkwardness and reputation, it’s about saftey

See:

It's also why double opt-in intros are *a must*. Please please please don't go intro'ing people to each other without asking first.

— Lara Hogan (@lara_hogan) August 5, 2017

Just because you have someone’s email address in your book doesn’t mean you should be giving it out to anyone that asks. Better to just assume any contact info you have for someone else is extremely private and only to be shared with their permission.


Double Opt-In Email Intros is a post from CSS-Tricks

Implementing Push Notifications: Setting Up & Firebase

Post pobrano z: Implementing Push Notifications: Setting Up & Firebase

You know those the little notification windows that pop up in the top right (Mac) or bottom right (Windows) corner when, for example, a new article on our favorite blog or a new video on YouTube was uploaded? Those are push notifications.

Part of the magic of these notifications is that they can appear even when we’re not currently on that website to give us that information (after you’ve approved it). On mobile devices, where supported, you can even close the browser and still get them.

Article Series:

  1. Setting Up & Firebase (You are here!)
  2. The Back End (Coming soon!)
Notification on Mac via Chrome
Push notification on a Mac in Chrome

A notification consists of the browser logo so the user knows from which software it comes from, a title, the website URL it was sent from, a short description, and a custom icon.

We are going to explore how to implement push notifications. Since it relies on Service Workers, check out these starting points if you are not familiar with it or the general functionality of the Push API:

What we are going to create

Preview of the our push notification demo website

To test out our notifications system, we are going to create a page with:

  • a subscribe button
  • a form to add posts
  • a list of all the previously published posts

A repo on Github with the complete code can be found here and a preview of the project:

View Demo Site

And a video of it working:

Gathering all the tools

You are free to choose the back-end system which suits you best. I went with Firebase since it offers a special API which makes implementing a push notification service relatively easy.

We need:

In this part, we’ll only focus on the front end, including the Service Worker and manifest, but to use Firebase, you will also need to register and create a new project.

Implementing Subscription Logic

HTML

We have a button to subscribe which gets enabled if 'serviceWorker' in navigator. Below that, a simple form and a list of posts:

<button id="push-button" disabled>Subscribe</button>

<form action="#">
  <input id="input-title">
  <label for="input-title">Post Title</label>
  <button type="submit" id="add-post">Add Post</button>
</form>

<ul id="list"></ul>

Implementing Firebase

To make use of Firebase, we need to implement some scripts.

<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-database.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-messaging.js"></script>

Now we can initialize Firebase using the credentials given under Project Settings → General. The sender ID can be found under Project Settings → Cloud Messaging. The settings are hidden behind the cog icon in the top left corner.

firebase.initializeApp({
    apiKey: '<API KEY>',
    authDomain: '<PROJECT ID>.firebaseapp.com',
    databaseURL: 'https://<PROJECT ID>.firebaseio.com',
    projectId: '<PROJECT ID>',
    storageBucket: '<PROJECT ID>.appspot.com',
    messagingSenderId: '<SENDER ID>'
})

Service Worker Registration

Firebase offers its own service worker setup by creating a file called `firebase-messaging-sw.js` which holds all the functionality to handle push notifications. But usually, you need your Service Worker to do more than just that. So with the useServiceWorker method we can tell Firebase to use our own `service-worker.js` file as well.

Now we can create a userToken and a isSubscribed variable which will be used later on.

const messaging = firebase.messaging(),
      database  = firebase.database(),
      pushBtn   = document.getElementById('push-button')

let userToken    = null,
    isSubscribed = false

window.addEventListener('load', () => {

    if ('serviceWorker' in navigator) {

        navigator.serviceWorker.register('https://cdn.css-tricks.com/service-worker.js')
            .then(registration => {

                messaging.useServiceWorker(registration)

                initializePush()
            })
            .catch(err => console.log('Service Worker Error', err))

    } else {
        pushBtn.textContent = 'Push not supported.'
    }

})

Initialize Push Setup

Notice the function initializePush() after the Service Worker registration. It checks if the current user is already subscribed by looking up a token in localStorage. If there is a token, it changes the button text and saves the token in a variable.

function initializePush() {

    userToken = localStorage.getItem('pushToken')

    isSubscribed = userToken !== null
    updateBtn()

    pushBtn.addEventListener('click', () => {
        pushBtn.disabled = true

        if (isSubscribed) return unsubscribeUser()

        return subscribeUser()
    })
}

Here we also handle the click event on the subscription button. We disable the button on click to avoid multiple triggers of it.

Update the Subscription Button

To reflect the current subscription state, we need to adjust the button’s text and style. We can also check if the user did not allow push notifications when prompted.

function updateBtn() {

    if (Notification.permission === 'denied') {
        pushBtn.textContent = 'Subscription blocked'
        return
    }

    pushBtn.textContent = isSubscribed ? 'Unsubscribe' : 'Subscribe'
    pushBtn.disabled = false
}

Subscribe User

Let’s say the user visits us for the first time in a modern browser, so he is not yet subscribed. Plus, Service Workers and Push API are supported. When he clicks the button, the subscribeUser() function is fired.

function subscribeUser() {

    messaging.requestPermission()
        .then(() => messaging.getToken())
        .then(token => {

            updateSubscriptionOnServer(token)
            isSubscribed = true
            userToken = token
            localStorage.setItem('pushToken', token)
            updateBtn()
        })
        .catch(err => console.log('Denied', err))

}

Here we ask permission to send push notifications to the user by writing messaging.requestPermission().

The browser asking permission to send push notifications.

If the user blocks this request, the button is adjusted the way we implemented it in the updateBtn() function. If the user allows this request, a new token is generated, saved in a variable as well as in localStorage. The token is being saved in our database by updateSubscriptionOnServer().

Save Subscription in our Database

If the user was already subscribed, we target the right database reference where we saved the tokens (in this case device_ids), look for the token the user already has provided before, and remove it.

Otherwise, we want to save the token. With .once('value'), we receive the key values and can check if the token is already there. This serves as second protection to the lookup in localStorage in initializePush() since the token might get deleted from there due to various reasons. We don’t want the user to receive multiple notifications with the same content.

function updateSubscriptionOnServer(token) {

    if (isSubscribed) {
        return database.ref('device_ids')
                .equalTo(token)
                .on('child_added', snapshot => snapshot.ref.remove())
    }

    database.ref('device_ids').once('value')
        .then(snapshots => {
            let deviceExists = false

            snapshots.forEach(childSnapshot => {
                if (childSnapshot.val() === token) {
                    deviceExists = true
                    return console.log('Device already registered.');
                }

            })

            if (!deviceExists) {
                console.log('Device subscribed');
                return database.ref('device_ids').push(token)
            }
        })
}

Unsubscribe User

If the user clicks the button after subscribing again, their token gets deleted. We reset our userToken and isSubscribed variables as well as remove the token from localStorage and update our button again.

function unsubscribeUser() {

    messaging.deleteToken(userToken)
        .then(() => {
            updateSubscriptionOnServer(userToken)
            isSubscribed = false
            userToken = null
            localStorage.removeItem('pushToken')
            updateBtn()
        })
        .catch(err => console.log('Error unsubscribing', err))
}

To let the Service Worker know we use Firebase, we import the scripts into `service-worker.js` before anything else.

importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-database.js')
importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-messaging.js')

We need to initialize Firebase again since the Service Worker cannot access the data inside our `main.js` file.

firebase.initializeApp({
    apiKey: "<API KEY>",
    authDomain: "<PROJECT ID>.firebaseapp.com",
    databaseURL: "https://<PROJECT ID>.firebaseio.com",
    projectId: "<PROJECT ID>",
    storageBucket: "<PROJECT ID>.appspot.com",
    messagingSenderId: "<SENDER ID>"
})

Below that we add all events around handling the notification window. In this example, we close the notification and open a website after clicking on it.

self.addEventListener('notificationclick', event => {
    event.notification.close()

    event.waitUntil(
        self.clients.openWindow('https://artofmyself.com')
    )
})

Another example would be synchronizing data in the background. Read Google’s article about that.

Show Messages when on Site

When we are subscribed to notifications of new posts but are already visiting the blog at the same moment a new post is published, we don’t receive a notification.

A way to solve this is by showing a different kind of message on the site itself like a little snackbar at the bottom.

To intercept the payload of the message, we call the onMessage method on Firebase Messaging.

The styling in this example uses Material Design Lite.

<div id="snackbar" class="mdl-js-snackbar mdl-snackbar">
  <div class="mdl-snackbar__text"></div>
  <button class="mdl-snackbar__action" type="button"></button>
</div>
import 'material-design-lite'

messaging.onMessage(payload => {

    const snackbarContainer = document.querySelector('#snackbar')

    let data = {
        message: payload.notification.title,
        timeout: 5000,
        actionHandler() {
            location.reload()
        },
        actionText: 'Reload'
    }
    snackbarContainer.MaterialSnackbar.showSnackbar(data)
})

Adding a Manifest

The last step for this part of the series is adding the Google Cloud Messaging Sender ID to the `manifest.json` file. This ID makes sure Firebase is allowed to send messages to our app. If you don’t already have a manifest, create one and add the following. Do not change the value.

{
  "gcm_sender_id": "103953800507"
}

Now we are all set up on the front end. What’s left is creating our actual database and the functions to watch database changes in the next article.

Article Series:

  1. Setting Up & Firebase (You are here!)
  2. The Back End (Coming soon!)

Implementing Push Notifications: Setting Up & Firebase is a post from CSS-Tricks

Be Slightly Careful with Sub Elements of Clickable Things

Post pobrano z: Be Slightly Careful with Sub Elements of Clickable Things

Say you want to attach a click handler to a <button>. You almost surely are, as outside of a <form>, buttons don’t do anything without JavaScript. So you do that with something like this:

var button = document.querySelector("button");
button.addEventListener("click", function(e) {
  // button was clicked
});

But that doesn’t use event delegation at all.

Event delegation is where you bind the click handler not directly to the element itself, but to an element higher up the DOM tree. The idea being that you can rip out and plop in new DOM stuff inside of there and not worry about events being destroyed and needing to re-bind them.

Say our button has a gear icon in it:

<button>
  <svg>
    <use xlink:href="#gear"></use>
  </svg>
</button>

And we bind it by watching for clicks way up on the document element itself:

document.documentElement.addEventListener("click", function(e) {
  
});

How do we know if that click happened on the button or not? We have the target of the event for that:

document.documentElement.addEventListener("click", function(e) {
  console.log(e.target);
});

This is where it gets tricky. In this example, even if the user clicks right on the button somewhere, depending on exactly where they click, e.target could be:

  • The button element
  • The svg element
  • The use element

So if you were hoping to be able to do something like this:

document.documentElement.addEventListener("click", function(e) {
  if (e.target.tagName === "BUTTON") {
    // may not work, because might be svg or use
  }
});

Unfortunately, it’s not going to be that easy. It doesn’t matter if you check for classname or ID or whatever else, the element itself that you are expecting might just be wrong.

There is a pretty decent CSS fix for this… If we make sure nothing within the button has pointer-events, clicks inside the button will always be for the button itself:

button > * {
  pointer-events: none;
}

This also prevents a situation where other JavaScript has prevented the event from bubbling up to the button itself (or higher).

document.querySelector("button > svg").addEventListener("click", function(e) {
  e.stopPropagation();
  e.preventDefault();
});

document.querySelector("button").addEventListener("click", function() {
  // If the user clicked right on the SVG, 
  // this will never fire
});

Be Slightly Careful with Sub Elements of Clickable Things is a post from CSS-Tricks

Using ES2017 Async Functions

Post pobrano z: Using ES2017 Async Functions

ES2017 was finalized in June, and with it came wide support for my new favorite JavaScript feature: async functions! If you’ve ever struggled with reasoning about asynchronous JavaScript, this is for you. If you haven’t, then, well, you’re probably a super-genius.

Async functions more or less let you write sequenced JavaScript code, without wrapping all your logic in callbacks, generators, or promises. Consider this:

function logger() {
    let data = fetch('http://sampleapi.com/posts')
    console.log(data)
}

logger()

This code doesn’t do what you expect. If you’ve built anything in JS, you probably know why.

But this code does do what you’d expect.

async function logger() {
    let data = await fetch('http:sampleapi.com/posts')
    console.log(data)
}

logger()

That intuitive (and pretty) code works, and its only two additional words!

Async JavaScript before ES6

Before we dive into async and await, it’s important that you understand promises. And to appreciate promises, we need go back one more step to just plain ol’ callbacks.

Promises were introduced in ES6, and made great improvements to writing asynchronous code in JavaScript. No more „callback hell”, as it is sometimes affectionately referred to.

A callback is a function that can be passed into a function and called within that function as a response to any event. It’s fundamental to JS.

function readFile('file.txt', (data) => {
  // This is inside the callback function
  console.log(data)
}

That function is simply logging the data from a file, which isn’t possible until the file is finished being read. It seems simple, but what if you wanted to read and log five different files in sequence?

Before promises, in order to execute sequential tasks, you would need to nest callbacks, like so:

// This is officially callback hell
function combineFiles(file1, file2, file3, printFileCallBack) {
    let newFileText = ''
    readFile(string1, (text) => {
        newFileText += text
        readFile(string2, (text) => {
            newFileText += text
            readFile(string3, (text) => {
                newFileText += text
                printFileCallBack(newFileText)
            }
        }
    } 
}

It hard to reason about and difficult to follow. This doesn’t even include error handling for the entirely possible scenario that one of the files doesn’t exist.

I Promise it gets better (get it?!)

This is where a Promise can help. A Promise is a way to reason about data that doesn’t yet exist, but you know it will. Kyle Simpson, author of You Don’t Know JS series, is well known for giving async JavaScript talks. His explanation of promises from this talk is spot on: It’s like ordering food a fast-food restaurant.

  1. Order your food.
  2. Pay for your food and receive a ticket with an order number.
  3. Wait for your food.
  4. When your food is ready, they call your ticket number.
  5. Receive the food.

As he points out, you may not be able to eat your food while you’re waiting for it, but you can think about it, and you can prepare for it. You can proceed with your day knowing that food is going to come, even if you don’t have it yet, because the food has been „promised” to you. That’s all a Promise is. An object that represents data that will eventually exist.

readFile(file1)
  .then((file1-data) => { /* do something */ })
  .then((previous-promise-data) => { /* do the next thing */ })
  .catch( /* handle errors */ )

That’s the promise syntax. Its main benefit is that it allows an intuitive way to chain together sequential events. This basic example is alright, but you can see that we’re still using callbacks. Promises are just thin wrappers on callbacks that make it a bit more intuitive.

The (new) Best Way: Async / Await

A couple years ago, async functions made their way into the JavaScript ecosystem. As of last month, its an official feature of the language and widely supported.

The async and await keywords are a thin wrapper built on promises and generators. Essentially, it allows us to „pause” our function anywhere we want, using the await keyword.

async function logger() {
  // pause until fetch returns
  let data = await fetch('http://sampleapi.com/posts')
  console.log(data)
}

This code runs and does what you’d want. It logs the data from the API call. If your brain didn’t just explode, I don’t know how to please you.

The benefit to this is that it’s intuitive. You write code the way your brain thinks about it, telling the script to pause where it needs to.

The other advantages are that you can use try and catch in a way that we couldn’t with promises:

async function logger ()  {
    try {
        let user_id = await fetch('/api/users/username')
        let posts = await fetch('/api/`${user_id}`')
        let object = JSON.parse(user.posts.toString())
        console.log(posts)
    } catch (error) {
        console.error('Error:', error) 
    }
}

This is a contrived example, but it proves a point: catch will catch the error that occurs in any step during the process. There are at least 3 places that the try block could fail, making this by far the cleanest way to handle errors in async code.

We can also use async functions with loops and conditionals without much of a headache:

async function count() {
    let counter = 1
    for (let i = 0; i < 100; i++) {
        counter += 1
        console.log(counter)
        await sleep(1000)
    }
}

This is a silly example, but that will run how you’d expect and it’s easy to read. If you run this in the console, you’ll see that the code will pause on the sleep call, and the next loop iteration won’t start for one second.

The Nitty Gritty

Now that you’re convinced of the beauty of async and await, lets dive into the details:

  • async and await are built on promises. A function that uses async will always itself return a promise. This is important to keep in mind, and probably the biggest „gotcha” you’ll run into.
  • When we await, it pauses the function, not the entire code.
  • async and await are non-blocking.
  • You can still use Promise helpers such as Promise.all(). Here’s our earlier example:
    async function logPosts ()  {
        try {
            let user_id = await fetch('/api/users/username')
            let post_ids = await fetch('/api/posts/<code>${user_id}')
            let promises = post_ids.map(post_id => {
                return  fetch('/api/posts/${post_id}')
            }
            let posts = await Promise.all(promises)
            console.log(posts)
        } catch (error) {
            console.error('Error:', error) 
        }
    }
  • Await can only be used in functions that have been declared Async.
  • Therefore, you can’t use await in the global scope.
    // throws an error
    function logger (callBack) {
        console.log(await callBack)
    }
    
    // works!
    async function logger () {
        console.log(await callBack)
    }

Available now!

The async and await keywords are available in almost every browser as of June 2017. Even better, to ensure your code works everywhere, use Babel to preprocess your JavaScript into and older syntax that older browsers do support.

If you’re interested in more of what ES2017 has to offer, you can see a full list of ES2017 features here.


Using ES2017 Async Functions is a post from CSS-Tricks