Tag Archiv: tutorials

Coloring Your Images With Duotone Filters

Created by Alex Lende

Beginning with WordPress 5.8, you can colorize your image and cover blocks with duotone filters! Duotone can add a pop of color to your designs and style your images to integrate well with your themes.

Filters? Like on Instagram?

Duotone doesn’t work in quite the same way as Instagram filters. Whereas Instagram filters do color adjustments (color levels/curves and sometimes a vignette for the photo editors among us), the new duotone filters entirely replace the colors of your images.

Photo by Charles Pragnell.

You can think of the duotone effect as a black and white filter, but instead of the shadows being black and the highlights being white, you pick your own colors for the shadows and highlights.

For example, a grayscale filter can be created by selecting black and white as shadow/highlight colors, and a sepia filter by choosing brown and tan.

Analogous colors can add a subtle effect and work well for cover backgrounds where the overlaid text still needs to stand out.

Much more vibrant and interesting effects can be made with complementary colors.

How Do I Add Duotone Filter?

The duotone effect works best on high-contrast images, so start with an image with a lot of large dark and light areas. From the block toolbar, use the filter button and choose a preset:

You can also choose colors from your theme’s palette, or a custom color of your choice.

In addition to the image block, duotone can be applied to both images and video in the cover block.

Duotone

Will This Overwrite Images in My Media Library?

Images and videos in your media library will remain unchanged. The duotone effect works using SVG filters and the CSS filter property, so the image or video is never modified in your library. On the one hand, this means that you can apply a filter to an image that you link to that doesn’t exist in your media library. On the other hand, this means that the filter won’t show up in RSS feeds or places that use the image URL directly.

Can I Add Duotone Colors to Blocks or Themes That I Develop?

The API for adding duotone colors to blocks is experimental in Gutenberg v10.6. Still, the documentation for using it in your own blocks can be found and will be updated under Supports Color in the Block Editor Handbook. Themes can add duotone presets with theme.json. More information can be found under Global Settings & Styles Presets in the Block Editor Handbook.

Try it Out Now Using the Gutenberg plugin

The duotone feature was released in version 10.6 of the Gutenberg plugin, so you can try it out now prior to the WordPress 5.8 release in July.


Thanks to @joen and @mkaz for assistance writing and reviewing this post.

So you want to make block patterns?

If you’ve ever built something for the WordPress block editor — a theme or a plugin — you may have also heard about block patterns.

Looking at the patterns that come bundled with WordPress, I thought it would be nice to dedicate to them a short post. They’re pretty nice, useful shortcuts when you know them, but there’s a good chance you may not know what they are or why you might want to use them.

What’s a block pattern?

Patterns are collections of pre-arranged blocks that can be combined and arranged in many ways making it easier to create beautiful content. They act as a head-start, leaving you to plug and play with your content as you see fit and be as simple as single blocks or as complex as a full-page layout.

They live in a tab in the block library. You can click or drag and you’re able to preview them with your site’s styles.

Basically, a block pattern is just a bunch of blocks put together in advance:

	<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:separator {"className":"is-style-default"} -->
<hr class="wp-block-separator is-style-default"/>
<!-- /wp:separator -->
<!-- wp:image {"align":"center","id":553,"width":150,"height":150,"sizeSlug":"large","linkDestination":"none","className":"is-style-rounded"} -->
<div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img src="https://blockpatterndesigns.mystagingwebsite.com/wp-content/uploads/2021/02/StockSnap_HQR8BJFZID-1.jpg" alt="" class="wp-image-553" width="150" height="150"/></figure></div>
<!-- /wp:image -->
<!-- wp:quote {"align":"center","className":"is-style-large"} -->
<blockquote class="wp-block-quote has-text-align-center is-style-large"><p>"Contributing makes me feel like I'm being useful to the planet."</p><cite>— Anna Wong, <em>Volunteer</em></cite></blockquote>
<!-- /wp:quote -->
<!-- wp:separator {"className":"is-style-default"} -->
<hr class="wp-block-separator is-style-default"/>
<!-- /wp:separator --></div></div>
<!-- /wp:group -->

That’s also how you create them: just use the block editor to configure a smattering of blocks to your liking, and the hard part’s over.

How do I get them in the block library?

There’s more documentation in the handbook, but what it boils down to is this:

<?php 
/*
Plugin Name: Quote Pattern Example Plugin
*/

register_block_pattern(
	'my-plugin/my-quote-pattern',
	array(
		'title'       => __( 'Quote with Avatar', 'my-plugin' ),
		'categories'  => array( 'text' ),
		'description' => _x( 'A big quote with an avatar".', 'Block pattern description', 'my-plugin' ),
		'content'     => '<!-- wp:group --><div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --><!-- wp:image {"align":"center","id":553,"width":150,"height":150,"sizeSlug":"large","linkDestination":"none","className":"is-style-rounded"} --><div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img src="https://blockpatterndesigns.mystagingwebsite.com/wp-content/uploads/2021/02/StockSnap_HQR8BJFZID-1.jpg" alt="" class="wp-image-553" width="150" height="150"/></figure></div><!-- /wp:image --><!-- wp:quote {"align":"center","className":"is-style-large"} --><blockquote class="wp-block-quote has-text-align-center is-style-large"><p>"Contributing makes me feel like I\'m being useful to the planet."</p><cite>— Anna Wong, <em>Volunteer</em></cite></blockquote><!-- /wp:quote --><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --></div></div><!-- /wp:group -->',
	)
);

?>

👆 That’s a snippet of PHP, which means you can drop it in a WordPress plugin, or perhaps more simply, paste it into the functions.php file from your theme. Done:

For patterns that include images, it’s worth thinking about where those are stored. The TT1 Blocks theme (which is a fancy name for “TwentyTwentyOne Blocks”) stores images in the theme library.

Now what?

The thing about a block pattern is, as soon as you insert it from the block library, it stops being a cohesive unit — now it’s just a smattering of blocks, detached from the pattern you created and meant to be customized to your liking. It’s a shortcut, not a template. That also means you don’t have to worry about switching themes or deactivating pattern plugins: the blocks you already inserted won’t go anywhere.

That being said, if you like this one pattern so much you want to use it again and again, with no customization at all, you can make it into a reusable block:

Reusable blocks are created, as the name implies, to be reused. The feature is a great way to store small bits of commonly used snippets that you can edit in one place to update in all. “Follow me on Twitter,” “Article series, or “Subscribe to my podcast” are great examples of that.

What makes a good block pattern?

Patterns, as they ship today, are limited by the features available. If the block editor doesn’t allow you to customize letter-spacing, your block pattern can’t either. While the Global Styles project will expand what’s to blocks, in the meantime, we have to work with the available tools.

Even then, with the most basic ingredients — color, photography, typography — it is possible to do a lot:

Three columns with images and text
Media and text with image on the right

I designed these patterns to potentially land in WordPress core, which all have a few properties in common:

They share a theme.

You can think of a pattern as a section of a website: it is meant to be part of a whole, and so it works best when it can exist in the context of other patterns that share the same theme. There are a few sharing a Nature theme in the patterns above, a few sharing an Art theme, and others sharing an Architecture theme. When seen together, it becomes easier to see how you might be able to piece together multiple pages of your site, one page at a time.

They share a minimalist color palette.

By being parts of a whole, patterns will inevitably land in a context that uses different colors. With a reduced color palette, there’s both a better chance of fitting in and less to customize to make it just right.

The best patterns do things you might have not done otherwise.

Whether that’s images offset to create a unique silhouette, or just using less visible features (like fixed positioning in the Cover block), it’s a way to surface creativity.

Tip: You can use any block in your patterns, including blocks that came from a plugin. And if that block is in the block directory, it will prompt you to install it with one click if it’s missing from your self-hosted WordPress:

Here’s a plugin for you

<?php 
/*
Plugin Name: Quote Pattern Example Plugin
*/

register_block_pattern(
	'my-plugin/my-quote-pattern',
	array(
		'title'       => __( 'Quote with Avatar', 'my-plugin' ),
		'categories'  => array( 'text' ),
		'description' => _x( 'A big quote with an avatar".', 'Block pattern description', 'my-plugin' ),
		'content'     => '<!-- wp:group --><div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --><!-- wp:image {"align":"center","id":553,"width":150,"height":150,"sizeSlug":"large","linkDestination":"none","className":"is-style-rounded"} --><div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img src="https://blockpatterndesigns.mystagingwebsite.com/wp-content/uploads/2021/02/StockSnap_HQR8BJFZID-1.jpg" alt="" class="wp-image-553" width="150" height="150"/></figure></div><!-- /wp:image --><!-- wp:quote {"align":"center","className":"is-style-large"} --><blockquote class="wp-block-quote has-text-align-center is-style-large"><p>"Contributing makes me feel like I\'m being useful to the planet."</p><cite>— Anna Wong, <em>Volunteer</em></cite></blockquote><!-- /wp:quote --><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --></div></div><!-- /wp:group -->',
	)
);

?>

In case you want to make patterns, this example plugin features two of the patterns you saw above. Drop it in your plugins folder and they should show up in your block library.

Installed pattern under “Text” Category

Feel free to tweak it, customize it, and make it yours. It’s GPL, after all!


Thank you @joen for the help writing this post.

Did You Know About Reusable Blocks?

Created by Joen Asmussen, @joen

The WordPress block editor (a.k.a. Gutenberg) comes with a feature called “reusable blocks.” They are blocks, saved for later, edited in one place.

Have you ever wanted to:

  • Re-use the same snippet of text across posts and pages?
  • Save complex layouts to spare you having to copy/paste from one post to another?

Reusable blocks can do these things.

Like templates, you mean?

Not quite. Think of reusable blocks as snippets of globally synchronized content that are personal to you. You can edit all your reusable blocks in one place, and any post or page you inserted that block into, get the updated version as well. 

Where you might use templates to structure your website, you can use reusable blocks to structure your content. For example:

  • A testimonial on your homepage and your product page.
  • A “this post is part of a series” box that you insert part-way through your article.
  • A “Follow me on social media” section you can weave into the prose of your popular article.
  • Complex but static blocks, such as a “Subscribe to my newsletter” box, a contact form, a survey, quiz, or polls.

Key properties are that reusable blocks are unbeatable when you want to reuse a snippet of content, edit it in one place, and have the changes propagate to every instance.

Show me how

To create a reusable block, open the block editor and create the content you want to reuse:

Now select the content you want to turn into a reusable block, then click the three-dot “More” menu and choose “Add to Reusable blocks.”

Voilà, you’ve now created a reusable block. From now on, you can find this block, and any other you create, in the “Reusable blocks” tab in the block library:

This is also where you can insert the newly created block on any of your posts or pages.

Where do I edit my existing reusable blocks?

To edit a reusable block, select it and make your edits. When you make an edit, the Publish button will have a little dot indicator:

This dot indicates you’ve made a global change that potentially affects posts beyond just the one you’re editing, the same as when you’re editing templates. This lets you confirm the change was intentional.

Another way to edit your reusable blocks is to click the global three-dot “More” menu and selecting “Manage all reusable blocks”:

This takes you to a section letting you edit, rename, export, or delete every reusable block you created. 

What else can I do?

Here are a couple of tips and tricks you can leverage to get the most out of reusable blocks.

Give them a good name

When you name a reusable block, you are essentially choosing your search terms, as the name is what you search for in the block library (or when you use the “slash command,” typing / in an empty paragraph):

Avoid names such as “Gallery” or “Image,” as that’ll be annoying when you just want to insert one of those. You can avoid that with a unique name, such as “My author biography.”

Insert in the best place of your content flow

One obvious benefit of reusable blocks is that they are just blocks, just like everything else in the block editor. That means you can insert it anywhere in your content. You might want your rich author biography to sit at the top or bottom of the post, but This post is part of a series box that might sit well two or three paragraphs not to disrupt the reading flow.

A design shortcut

Maybe you created a complex layout you’re happy with, a call to action with the right image and buttons, and it took a while to get it just right. Go on and save it as a reusable block: even if you mean to insert it only to convert it to a regular block, it might still save you a minute. 

To convert a reusable block to regular (blocks, select it and click the “Convert to regular blocks”:

Design by Beatriz Fialho.

Tip: You can also find some nice patterns on Gutenberg Hub or ShareABlock.

Take it with you

Need to move to another site? You can both export and import reusable blocks. Go to the Manage all reusable blocks section from the global three-dot “More” menu, hover over the block you want to export, and click “Export as JSON”:

The downloaded file can be imported on any WordPress 5.0 or newer website.

Try it

Create a draft post and play around with Reusable Blocks to see how you might start using them. You can always delete them when you’re done playing.

You can test importing and using a small reusable block I created as an example. It’s a “Further reading” block that shows the four latest posts from the category “Featured”:

It might work well as a highlight in an article, giving the reader something new to read or awareness of your other content.

The videos in this post show the reusable blocks flow in the upcoming WordPress 5.7.

Download the block from this gist, import it to your WordPress site, then customize to make it yours.

Getting Started With The JavaScript Web Animation API

js-animations

Adding animations to web interfaces makes pages and apps feel more responsive and interactive. A side menu that smoothly slides out of view provides a much better user experience then a menu that just disappears when you close it.

So far creating web animations was done either via CSS transitions, CSS keyframes, or an external library such as Animate.css or Velocity. Thanks to a new native JavaScript API, we are now able to freely animate any HTML element without ever having to leave our .js file.

Creating Animations

To showcase the awesomeness of the new API, let’s build a super simple example, once the old-fashioned CSS way, then with JavaScript Web Animations.

The editor below contains two HTML divs that when clicked on move to the right and then change their color. The square is animated via CSS @keyframes, and the circle via the Web Animations API.

(Play with our code editor on Tutorialzine.com)

The @keyframes animation should be familiar to most developers so let’s look at that first.

The CSS Approach

Our CSS animation is defined in a @keyframes block that represents a timeline of all the transitions. Once we have our choreography defined, we can map it to a selector via the animation property and it’s options.

.animate {
    animation-name: move-and-change-color;   
    animation-duration: 0.4s;
    animation-fill-mode: forwards;
}

@keyframes move-and-change-color {
    0% {
        transform: translateX(0);
    }

    80% {
        transform: translateX(100px);
        background-color: #2196F3;
    }

    100% {
        transform: translateX(100px);
        background-color: #EF5350;
    }
}

We want the animation to start on user interaction so we will also have to create an on-click event listener that adds a CSS class to the desired element:

var square = document.getElementById('square');
    
square.addEventListener('click', function() {
    square.className += " animate";
});

Although it works pretty well, the CSS approach seems rather non-intuitive as we define what happens in the stylesheets, but actually start it in the JavaScript. We also have very limited control over the animation once it has been invoked. Both these problems can be solved by switching to the Web Animation API.

The JavaScript Approach

We can describe our JavaScript animation using almost the exact same transitions we used in the CSS example:

var moveAndChangeColor = [
    { 
        transform: 'translateX(0)',
        background: '#2196F3'    // blue
    },
    { 
        offset: 0.8,
        transform: 'translateX(100px)', 
        background: '#2196F3'    // blue
    },
    {
        transform: 'translateX(100px)',
        background: '#EF5350'    // red
    }
];

Each object in the array represents a state of the animation. The states are evenly distributed in time (3 states – 0%, 50%, 100%) unless we change the timing using the offset option, as we’ve done with the middle state.

After we’ve defined our animation array, we can invoke it using the animate() method. It takes as a second argument an object with the same options as the CSS animation property, although with slightly different names (e.g. animation-fill-mode is fill, animation-iteration-count is iteration, etc).

var circle = document.getElementById('circle');
  
circle.addEventListener('click', function() {
    circle.animate(moveAndChangeColor, {
        duration: 400,
        fill: 'forwards'
    });
});

As you can see, the JavaScript approach is much more organized with the animation stored in a variable and the animate() method used for invoking it whenever we need to.

Controlling Animations

The Web Animation API also makes it possible to easily control the playback of an animation in a number of ways. The animate() method returns an Animation object which we can save in a variable and use to refer to that animation later on.

var animation = elem.animate(transitions, options);

The interface provides us with the following methods:

  • pause() – Freezes the animation in its current state.
  • play() – Resumes the animation or restarts it if it has finished.
  • reverse() – Plays the transitions backwards.
  • finish() – Goes to the end of the animation (or the beginning if reversed).
  • cancel() – Stops playback and returns to starting state.

Below is a tiny demo with a loading indicator that loops infinitely. We’ve setup buttons for the different events so that you can try them out:

(Play with our code editor on Tutorialzine.com)

Properties and Events Listeners

The Animation object returned from animate() holds several useful properties that give us access to options like the current time, the playback rate, and others. Although some are read-only, most of the properties can be used as setters and getters.

You can view the JS code in the editor below to get a sense of how they work. For the full list of properties visit MDN.

(Play with our code editor on Tutorialzine.com)

In addition to that, the Web Animation API provides us with two useful event handlers for when the animation has finished or has been canceled:

spinnerAnimation.addEventListener('finish', function() {
    // Animation has completed or .finish() has been called.
    doSomething();
});

spinnerAnimation.addEventListener('cancel', function() {
    // Animation has been canceled.    
    doSomething();
});

Support and Performance

Most of the Web Animation features are freely available in Chrome and Firefox, with Edge and Safari implementations in the working (caniuse). There is also a well maintained open-source polyfill which can be used while waiting for full browser coverage.

When it comes to performance, there shouldn’t be any difference compared to regular CSS transitions, as browsers use the same engine for both. If you stick to animating only properties that don’t cause redraws, such as transform and opacity, animations should keep a steady 60fps rate.

Conclusion

The Web Animation API gives developers an awesome new way to create and control web animations using nothing but pure JavaScript. For animations that are invoked on user interaction or other dynamic events, this is great news since the whole animation can be done in the controller code, without having to jump to a CSS file for the actual transitions.

This article covered most of the features of the new API, but if you want to learn more here are a couple of excellent resources we highly recommend:

Handle Mouse And Touch Input With The Pointer Events API

pointer-events-api-2

With more and more people using their mobile phones and tablets for web browsing, we as developers have to make sure that our web interfaces are fully accessible via touch. Setting up click and hover event listeners sort of works, but it is clearly a leftover solution from the mouse era.

Thankfully, there is a new API in town that accommodates the needs of mouse, touch, and stylus devices. It’s called Pointer events (not to be mistaken with the CSS property of the same name) and it allows us to add event listeners that are better suited for working with all types on input.

Meet The New Events

The new Pointer Event API is an evolved version of the Mouse Event interface we’ve all been using so far. It extends the functionality of the old API and adds support for multi-touch gestures, precise pen input, and overall smoother touchscreen interaction.

Most of the Pointer Events have direct alternatives among the old mouse events. Once the new API gets full browser support we can directly substitute with the more modern alternatives:

const button = document.querySelector("button");

// Instead of mouseover
button.addEventListener('mouseover', doSomething);

// We can use pointerover
button.addEventListener('pointerover', doSomething);

Interacting with a mouse should be the same in both cases. Using fingers or a stylus, however, will be easier to program with the new API.

Recognizing Input Type

An awesome feature of the Pointer Events API is that it can tell which type of input has been used. This can be helpful when you want to ignore some of the input methods or provide special feedback for each one.

button.addEventListener('pointereover', function(ev){
  switch(ev.pointerType) {
    case 'mouse':
      // The used device is a mouse or trackpad.
      break;
    case 'touch':
      // Input via touchscreen.
      break;
    case 'pen':
      // Stylus input.
      break;
    default:
      // Browser can't recognize the used device.
      break;
  }
});

Other Properties

The Pointer Events interface provides some some other interesting data as well. It includes all the MouseEvent properties plus the following:

  • pointerId – Unique ID for the pointer causing the event.
  • width and height – Size of the contact area in pixels.
  • pressure – Pressure of touch, if available.
  • tiltX and tiltY – The angle at which a stylus is touching the screen.
  • isPrimary – Determines whether an event has been emitted by the pirmary pointer device.
Mouse clicks always have a width and height of 1, touch size differs.

Mouse clicks always have a width and height of 1 while touch size varies.

Browser Support

Pointer Events are fairly new, so browser compatibility isn’t perfect yet. Chrome (desktop and mobile), Edge, IE, and Opera have full support; Firefox and Safari don’t.

Pointer Events Browser Compatibility on Can I Use

Pointer Events Browser Compatibility on Can I Use

To check whether a browser has the Pointer Events API you can use the window object:

if (window.PointerEvent) {
  // Pointer Events enabled.
} else {
  // Pointer Events not supported
}

A popular open-source pollyfill is also available for those who don’t want to wait for full browser adoption.

Conclusion

Although it doesn’t have full browser support yet, the Pointer Events API is eventually going to take over the old mouse events. It provides a lot of cool features that will increase web accessibility and enable developers to create more advanced touch and stylus-based apps.

If you want to learn more about the Power Events API we recommend checking out these resources:

Learn Webpack in 15 Minutes

learn-webpack-in-15

Build tools have become an integral part of web development, mainly due to the ever-increasing complexity of JavaScript apps. Bundlers allow us to package, compile, and organize the many assets and libraries needed for a modern web project.

In this tutorial we will take a look at webpack, a powerful open-source bundler and preprocessor that can handle a huge variety of different tasks. We’ll show you how to write modules, bundle code, and use some of the loader plugins. The tutorial is designed for total beginners to webpack, but having some JavaScript knowledge is advised.

webpack-logo

Why webpack?

Much like any other aspect of web development, there isn’t a standard for which build tool to use. Right now, developers have to choose between webpack, Gulp, Browserify, NPM scripts, Grunt, and like 10 others. There are many in-depth comparisons out there, but all of these tools are very similar, so most of the time it comes down to personal preference and what project you are working on.

Here are some pros and cons to help you decide whether webpack is the tool for you:

Pros:

  • Great for working with singe-page apps
  • Accepts both require() and import module syntaxes
  • Allows for very advanced code splitting
  • Hot Reload for quicker development with React, Vue.js and similar frameworks
  • Мost popular build tool according to the 2016 JavaScript survey

Cons:

  • Not suitable for beginners in web development
  • Working with CSS files, images, and other non-JS resources is confusing at first
  • Documentation could be better
  • Changes a lot, even most 2016 tutorials are already outdated

1. Installation

The easiest way to install webpack is by using a package manager. We will go with npm but feel free to use Yarn or another hip alternative. In both cases you need to have Node.js on your machine and a package.json ready to go.

It is preferred to install it locally (without the -g tag). This will make sure everyone working on your project has the same version of webpack.

npm install webpack --save-dev

Once we have it installed, it’s best to run webpack via a Node.js script. Add these lines to your package.json:

//...
    "scripts": {
        "build": "webpack -p",
        "watch": "webpack --watch"
    },
//...

Now by calling npm run build from the terminal we can make webpack bundle our files (the -p option stands for production and minifies the bundled code). Running npm run watch will start a process that automatically bundles our files when any of them change.

The last part of the setup is to tell webpack which files to bundle up. The recommended way to do this is by creating a config file.


2. Webpack Config File

Here we will look at the config file in its most basic form but don’t let that fool you – the webpack config file is quite powerful, varies a lot from project to project, and can become super complex in some cases.

In the root directory of your project add a file called webpack.config.js.

webpack.config.js

var path = require('path');

module.exports = {
  entry: './assets/js/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

The entry option tells webpack which is our main JavaScript file. There are many different strategies for configuring entry points but in most cases a single entry is enough.

In output we specify the name and path of our bundle. After running webpack we will have all our JavaScript in a file called bundle.js. This is the only script file that we will link in our HTML:

<script src="./dist/bundle.js"></script>

This setup should be enough to get us started. Later we will add some more stuff to it, but first let’s see how modules work.


3. Webpack Modules

Webpack provides multiple ways to work with modules, and most of the time you are free to go with whichever one you like. For this tutorial we will use the ES6 import syntax.

We want to add a module that greets our users. We create a file called greeter.js and make it export a simple function:

greeter.js

function greet() {
    console.log('Have a great day!');
};

export default greet;

To use this module, we have to import it and call it in our entry point, which if you look back at the config file is index.js.

index.js

import greet from './greeter.js';

console.log("I'm the entry point");
greet();

Now when we run the bundler with npm run build, and open our HTML in the browser, we see this:

console-greet

Our entry point and our greeter module were compiled into one file called bundle.js and it was executed by the browser. Here is a simple flow chart of what’s happening so far:

webpack-flow-1


4. Requiring Libraries

We want our app to specify which day of the week it is when it greets users. To do so we will use moment.js by directly importing it into our greeter module.

First we need to install the library via npm:

npm install moment --save

Then in our greeting module, we simply import the library exactly the same way we imported local modules in the previous point:

greeter.js

import moment from 'moment';

function greet() {
    var day = moment().format('dddd');
    console.log('Have a great ' + day + '!');
};

export default greet;

After we bundle up again to apply the changes, in the browser console we will have the following messages:

weekday-greet

Our flow diagram now looks like this:

webpack-flow-2

Note: There are other, more advanced techniques for including libraries but they are outside the scope of this article. You can read more about them here.


 5. Loaders

Loaders are webpack’s way to execute tasks during bundling and pre- or post-process the files in some manner. For example, they can compile TypeScript, load Vue.js components, render templates, and much more. Most loaders are written by the community, for a list of popular loaders go here.

Let’s say we want to add a linter to our project that checks our JS code for errors. We can do so by including the JSHint loader, which will catch all kinds of bad practices and code smells.

First we need to install both JSHint and the webpack JSHint loader:

npm install jshint jshint-loader --save

Afterwords, we are going to add a few lines to our webpack config file. This will initialize the loader, tell it what type of files to check, and which files to ignore.

webpack.config.js

var path = require('path');

module.exports = {
  entry: './assets/js/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  // Add the JSHint loader
  module: {
    rules: [{
      test: /\.js$/, // Run the loader on all .js files
      exclude: /node_modules/, // ignore all files in the node_modules folder
      use: 'jshint-loader'
    }]
  }
};

Now when webpack is started, it will show us a list of warnings in the terminal (which we will ignore):

terminal-warnings

Since moment.js is located in the node_modules folder, it won’t be linted by the JSHint loader:

webpack-flow-3


Further Reading

This concludes our introduction to webpack! Since this is a lesson for beginners, we tried to cover only the most useful and must-know concepts of webpack. We hope the tutorial has been helpful, not too confusing, and within the 15 minute limit from the title.

In the near future, we are planning to add a second part to this tutorial, explaining how to work with CSS modules and other more-advanced features. In the meantime, if you want to learn more about webpack (and there is a lot more) we recommend checking out these awesome resources:

Quick Tip: Accessing The Clipboard With JavaScript

accessing-the-clipboard-with-js_

In this article we’re going to show you how to use simple vanilla JavaScript snippets to:

  1. Add text to the clipboard on user action, such as the press of a button.
  2. Modify the content of the clipboard when a user copies something.

The APIs we will be using don’t require any external libraries, and have almost perfect browser compatibility!

Copy On Click

An awesome accessibility feature you can add to your website is the ability to copy strings directly via button press. This interaction can be applied to quickly grab URLs, long strings such as SSH keys, terminal commands, hex colors, or any other data that is frequently copy & pasted.

To make this happen we will need to use a cool JavaScript method called execCommand(). It allows us to invoke a number of different events that manipulate editable content such as making text bold/italic, doing undo/redo, and also copy/cut/paste.

document.execCommand('copy');

This works exactly like pressing CTRL/Cmd+C on your keyboard, so in order to copy any text we first need to have it selected. This is possible in JavaScript thanks to the Selection API, which allows us to programatically make a text selection from any HTML element on the page.

var button = document.getElementById("copy-button"),
    contentHolder = document.getElementById("content-holder");

button.addEventListener("click", function() {

    // We will need a range object and a selection.
    var range = document.createRange(),
        selection = window.getSelection();

    // Clear selection from any previous data.
    selection.removeAllRanges();

    // Make the range select the entire content of the contentHolder paragraph.
    range.selectNodeContents(contentHolder);

    // Add that range to the selection.
    selection.addRange(range);

    // Copy the selection to clipboard.
    document.execCommand('copy');

    // Clear selection if you want to.
    selection.removeAllRanges();

}, false);

To see the example in action check out the editor below:

(Play with our code editor on Tutorialzine.com)

In the example above the content we want to copy is simply stored in a paragraph. If the text you need is not on the page, you will need to first write it in an element hidden off-screen.

Modify Copied Text

Here we will show you how to manipulate content in the clipboard after it’s been copied. This can be very useful for escaping code, formatting numbers and dates, or for other text transformations such as uppercase, lowercase, etc.

JavaScript provides us with copy() and paste() events, but they are designed in such a way that the content stored in the clipboard is secure:

  • In the copy event handler we cannot read what’s stored in clipboard, as there may be personal info which we shouldn’t have access to. We can, however, overwrite the clipboard data.
  • In the paste event it’s the opposite: we can read the data, but we cannot change it.

Since we want to read and write at the same time, we will need to use the Selection API once more. Here is the solution:

document.addEventListener('copy', function(e){

    // We need to prevent the default copy functionality,
    // otherwise it would just copy the selection as usual.
    e.preventDefault();

    // The copy event doesn't give us access to the clipboard data,
    // so we need to get the user selection via the Selection API.
    var selection = window.getSelection().toString();

    // Transform the selection in any way we want.
    // In this example we will escape HTML code.
    var escaped = escapeHTML(selection);

    // Place the transformed text in the clipboard. 
    e.clipboardData.setData('text/plain', escaped);

});

You can try the code in the editor below:

(Play with our code editor on Tutorialzine.com)

Further Reading

In this quick tip we presented you two useful snippets for working with the clipboard in pure vanilla JavaScript. We used a bunch of hip native APIs, so here they are again if you want to read more about them:

  • execCommand – Execute actions such as copy, paste, cut, bold, italic, underline, delete, and many others. – MDN, Can I Use
  • Selection API – Allows developers to make a range selection from any text on the page. – MDN, Can I Use
  • JavaScript Copy Event – An event fired when users press CTRL/Cmd+C or choose “copy” from the right-click menu. – MDN, Can I Use

Also, if you need more control over copy/paste/cut events, you can use a library such as clipobard.js. It has lots of features and provides a nice clean API for managing the clipboard.

We hope you enjoyed this article! Feel free to ask question or leave suggestions in the comment section below :)

Everything You Should Know About Progressive Web Apps

progressive-web-apps_1

Progressive Web Apps is a web application which takes advantage of modern browser features and can be added to your homescreen, behaving just like a native application.

In this tutorial we’re going to show you everything you need to know about PWAs, step by step, with practical examples and a demo app. To not start from scratch, we are going to use the selfie app we made recently, and make it progressive.


What is a Progressive Web App

In its core a progressive web app isn’t any different from a normal website – it’s made of HTML, CSS and JavaScript, and lives in the browser. What separates PWAs from regular websites is a list of 10 key concepts that need to be fulfilled. Here they are, taken directly from the Google Developers website.

  1. Safe – Served via HTTPS to prevent snooping and ensure content hasn’t been tampered with.
  2. Progressive – Work for every user, regardless of browser choice because they’re built with progressive enhancement as a core tenet.
  3. Responsive – Fit any form factor: desktop, mobile, tablet, or whatever is next.
  4. Connectivity-independent – Enhanced with service workers to work offline or on low quality networks.
  5. App-like – Feel like an app to the user with app-style interactions and navigation because they’re built on the app shell model.
  6. Fresh – Always up-to-date thanks to the service worker update process.
  7. Discoverable – Are identifiable as “applications” thanks to W3C manifests and service worker registration scope allowing search engines to find them.
  8. Re-engageable – Make re-engagement easy through features like push notifications.
  9. Installable – Allow users to “keep” apps they find most useful on their home screen without the hassle of an app store.
  10. Linkable – Easily share via URL and not require complex installation.

Following these guidelines will ensure that your app works well not only when viewed in the browser, but also when started separately via a home screen shortcut. You may find the wording Google has chosen rather confusing, but don’t worry, we will explain the rules one by one later in the tutorial.


What a Progressive Web App is NOT

The concept of PWAs shouldn’t be confused with:

All of the aforementioned technologies wrap HTML apps and package them into executable files, be it an .apk, .exe or anything else, which then have to be downloaded from the respective app store and installed on the user’s device.

PWAs don’t require installation and aren’t available (yet) in Google Play or the iTunes App store. To download a PWA you need to simply visit it’s website and then save it to the home screen as a shortcut. Developing and maintaining separate iOS and Android versions is no longer an issue, but browser support needs to be taken into consideration.


1_safe1. Safe

Most progressive web apps work with native APIs and service workers, technologies that deal with sensitive data and need to be handled with caution. That’s why every PWA has to be served through a HTTPS connection.

If you don’t have access to a server with a SSL certificate, the easiest way run projects in a secure environment is via GitHub Pages or a similar service. Any GitHub repository can be hosted directly over HTTPS, and both GitHub and GitHub Pages are free for public repos.

This is where we’ve chosen to host our demo: https://tutorialzine.github.io/pwa-photobooth/.

For simple testing on a local server, you can also try Ngrok. Its a tiny tool that allows you to tunnel any currently running localhost to a secure public URL. Ngrok is free and available for Windows, Mac, and Linux.


2_progressive

2. Progressive

Essentially, what this means is that PWAs should use web technologies that are widely supported and work equally well on as many browsers as possible. As we all know, in the world of web development this is close to impossible, but still there are things we can do to cover a larger user base.

For example, in our PhotoBooth app we use the getUserMedia() API for accessing the hardware camera on a device. Its support in different browsers is quite inconsistent – Safari doesn’t support it at all, the browsers that do support it need prefixes and differ in usage.

To ensure more people can actually use our app, we cover all the prefixes:

navigator.getMedia = ( 
    navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia
);

We also show an error if none of the prefixes work:

if (!navigator.getMedia) {
    displayErrorMessage("Your browser doesn't have support for the navigator.getUserMedia interface.");
}
else {
    // Use Camera API
}

Fallbacks and polyfills should be provided where possible. The same principles go for the CSS and HTML code.


3_responsive

3. Responsive

The app should look nice on all devices, no matter their screen size. Our app has a fairly simple UI so we’ve used only a couple of media queries to control font-size, paddings, margins, etc.

Don’t be afraid to use CSS libraries and frameworks such as Bootstrap, as they make it really easy to form grids, and deal with typography and general responsiveness.


4_offline

4. Connectivity independent

This is an important one. Using service workers allows your app to work even when there is no internet connection available.

Some apps can be cached only partially: UI is cached and available offline, dynamic content still needs access to a server.

Others, like our PhotoBooth demo, can be cached in their entirety. All of the source code and resources will be saved locally and the app will work offline and online exactly the same way. Here is the code that makes the magic happen:

This is an oversimplified usage of Service Workers, use with caution in commercial projects.

First we need to make a service worker JavaScript file, and define the logic behind it.

sw.js

// Install the service worker.
this.addEventListener('install', function(event) {
    event.waitUntil(
        caches.open('v1').then(function(cache) {
            // The cache will fail if any of these resources can't be saved.
            return cache.addAll([
                // Path is relative to the origin, not the app directory.
                '/pwa-photobooth/',
                '/pwa-photobooth/index.html',
                '/pwa-photobooth/assets/css/styles.css',
                '/pwa-photobooth/assets/fonts/MaterialIcons-Regular.woff2',
                '/pwa-photobooth/assets/js/script.js',
                '/pwa-photobooth/assets/icons/ic-face.png',
                '/pwa-photobooth/assets/icons/ic-face-large.png',
                '/pwa-photobooth/manifest.json'
            ])
            .then(function() {
                console.log('Success! App is available offline!');
            })
        })
    );
});


// Define what happens when a resource is requested.
// For our app we do a Cache-first approach.
self.addEventListener('fetch', function(event) {
    event.respondWith(
        // Try the cache.
        caches.match(event.request)
        .then(function(response) {
            // Fallback to network if resource not stored in cache.
            return response || fetch(event.request);
        })
    );
});

Then we need to link that service worker to our HTML.

index.html

<script>
// Register Service Worker.

if ('serviceWorker' in navigator) {
    // Path is relative to the origin, not project root.
    navigator.serviceWorker.register('/pwa-photobooth/sw.js')
    .then(function(reg) {
        console.log('Registration succeeded. Scope is ' + reg.scope);
    })
    .catch(function(error) {
        console.error('Registration failed with ' + error);
    });
}
</script>

Now all of the files in our project will be saved in the user’s browser. Any JavaScript variables and object should also be saved in the localStorage or IndexDB where possible.

Right now Service Workers are supported in Chrome, Firefox and Opera. Safari and Edge are also working towards adopting them, and we hope that in the future they will be available in every browser.


5_applike

5. App-like

When building PWAs, it’s recommended to follow a design concept called app-shell architecture. It sounds very complicated but essentially boils down to this: the app is separated into two major components: the shell and the content.

The shell contains all the static UI elements such as a header, menus, drawers, etc. When we cache an app, the shell should always be saved on the device, because we want it to be available at all times. That way when a user with no internet connection opens the app, they won’t see an empty screen or a running dinosaur – they will see the cached app interface and an appropriate error message.

Image Courtesy To developers.google.com

The content resides within the shell. It can also be cached but it isn’t necessary to do so as content is usually dynamic, changes frequently and can be different on every single page load.


6_fresh_

6. Fresh

Once cached, our PWA will always load from the local storage. However, if we change the service worker sw.js in any way, on the next page load the new version will be downloaded and installed.

this.addEventListener('install', function(event) {
    event.waitUntil(
        caches.open('v1.0.1').then(function(cache) {
            // ...
        })
    );
});

Using service worker updates we can re-download resources, delete old cache, or completely change the service worker logic. You can learn more about the SW Update process from this Google Developers article – here.


7_discoverable_

7. Discoverable

By adding a Web Manifest to our app we can provide various information about it and change the way it is displayed on people’s devices. It allows apps to be saved to the home screen with a custom icon, to be started in a separate browser window, and a lot of other cool stuff.

The Web Manifest takes the form of a simple JSON file:

manifest.json

{
  "name": "Progressive Web App: PhotoBooth",
  "short_name": "PhotoBooth",
  "description": "Simple Progressive Web App for taking selfies.",
  "icons": [{
      "src": "assets/icons/ic-face.png",
      "type": "image/png",
      "sizes": "72x72"
    }, {
      "src": "assets/icons/ic-face-large.png",
      "type": "image/png",
      "sizes": "144x144 256x256" 
    }],
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "#fff",
  "theme_color": "#fff",
  "orientation": "portrait"
}

Most of the properties are self explanatory so we will cover only the more important ones. To see the full Web manifest format and all the available fields go here.

  • Shortname – This is the name our app will have when saved to the home screen.
  • Icons – Array of icons with different resolutions.
  • Display – Defines how the app will be opened. We’ve chosen standalone so when started our photo booth will appear in a full-screen window without any browser navigation or menus. It will also be seen as a separate app in multitasking.

To register the manifest we have to link it to our HTML:

<!-- Web Manifest -->
<link rel="manifest" href="manifest.json">

Safari doesn’t support the Web Manifest standard yet but we can define app-like behavior with this Apple-specific meta tag:

<!-- Meta tag for app-like behaviour in iOS -->
<meta name=”apple-mobile-web-app-capable” content=”yes”>

8_notifications

8. Re-engageable

Push notifications aren’t limited to native apps any more. Thanks to service workers and the Push API, web applications can also send messages to the Android notification bar. Not all apps will benefit from having this feature, but when used properly notifications can really help engage users.

This topic goes beyond the scope of our tutorial, as Push Notifications are quite complicated and deserve a full lesson on their own. If you still want to implement notifications to your web app, here are some of the best learning resources available:

  • Google Developers, Push Notifications: Timely, Relevant, and Precise – here.
  • Google Developers, Push Notifications on the Open Web – here.
  • MDN, Using the Push API – here.
  • Push.js, Library that provides a cleaner API for handling push notifications – here.

9_installable_

9. Installable

By default any website can be manually saved to the home screen using the Add to Home Screen button from the Chrome browser menu. However, it might be rather difficult to make users “install” our app this way, since most people don’t know about that feature at all.

Thankfully, there is a way for your app to prompt users to save it with a simple installation pop-up. To prevent developers from abusing these pop ups, there isn’t any way to programmatically show them. Instead, they will appear on their own when an app fulfills a series of requirements:

  1. There is a valid Web Manifest.
  2. There is a valid Service Worker installed.
  3. The app is served over HTTPS.

We have all of the above covered, so when a user visits our app’s website a couple of times, they will get this prompt:

Add To Homescreen Prompt

Add To Homescreen Prompt

The entire installation process of our app is in this simple prompt. The install happens instantly, and once saved the PhotoBooth will be available to launch from a home screen icon, behaving exactly like a native app.


10_linkable_

10. Linkable

Anyone with a web browser has access to PWA apps and they can be shared simply via their URL. No third party tools are required for finding or installing them.

If an app runs in standalone mode, it’s also advisable to add in-app share buttons, since the browser address bar and menus aren’t visible.


Conclusion

Our PWA is now complete. We can test how well it follows the PWA rules with an official Google-made tool called Lighthouse. It recreates possible scenarios and tests the app thoroughly. Here is what it tells us about the PhotoBooth:

Lighthouse Report

Lighthouse Report

We passed!

If you want to find more PWAs to play with, go to pwa.rocks. They offer a nice collection of games and useful tools, showcasing the great power of Progressive Web Apps.

Quick Tip: Working with the JavaScript Battery API

working-with-js-battery-api

In this tutorial we’re going to show you how to use the JavaScript Battery API to improve the user experience for people in desperate need of a charger. We’ll look at the Battery API itself, as well as some techniques for getting the most out of every drop of the most precious of resources!

Monitoring Battery Life

The JavaScript Battery Status API talks to the device’s hardware and gives us accurate data about the system’s charging state. It can be accessed via the promise based navigator.getBattery() interface, or directly via the navigtator.battery object, although the second option is now deprecated and overall not recommended.

Some browsers lack support for the Battery API (you guessed it, they are Safari and IE), so a quick support check can go a long way in terms of debugging:

if(navigator.getBattery){
    // Battery API available.
    // Rest of code goes here.
}
else{
    // No battery API support.
    // Handle error accordingly.
}

Once we are sure that your user can access the API, grabbing the needed information is really easy:

navigator.getBattery()
    .then(function(batteryManager) {
        
        // Get current charge in percentages.
        var level = batteryManager.level * 100;

    })
    .catch(function(e) {
        console.error(e);
    });

The getBattery() method returns a promise and resolves with a BatteryManager object containing various information about the current status of the hardware:

  • batteryManager.level – The current charge, returns a float between 0 and 1.
  • batteryManager.charging – Is the device on power supply or not, returns true/false.
  • batteryManager.chargingTime – Remaining time in seconds till completely charged.
  • batteryManager.dischargingTime – Remaining time until battery is dead.

It also provides events that can be used to monitor changes in any of the above properties.

  • BatteryManager.onlevelchange
  • BatteryManager.onchargingchange
  • BatteryManager.onchargingtimechange
  • BatteryManager.ondischargingtimechange

Combining the raw data with the event listeners, we can easily set up a watcher for low battery levels:

navigator.getBattery()
    .then(function(battery) {    
        battery.onlevelchange = function() {

            if(battery.level<30 && !battery.charging) {
                powerSavingMode = true;
            }

        }
    });

Once we know how much juice is left in the device we can adapt the app and turn on a power saving mode if its needed.

Preserving energy

The biggest battery drainer of all components is the screen. This is especially true on smartphones and tablets where often CPUs are energy preserving, while the screens have super-ultra-full-QHD resolution with the brightness of two suns.

The first and foremost thing we can do to address this issue is limit the amount of light the screen is emitting. JavaScript doesn’t have the authority to control the brightness directly, but we can do so by changing the color pallet to a darker theme.

dark-theme

Dark colors need less energy to be displayed, with more than 50% reduction on AMOLED screens.

The next thing we can do is limit the amount and size of requests to external resources. The biggest drainers here are high-res images, advertisements, and large JavaScript libraries, as they need a lot of bandwidth to download.

Here we have two options – load an alternative, more optimized resource with a smaller footprint, or, fully remove the image/advert if it doesn’t portray any essential information. Any background images, videos or animations should be removed.

removed-ads

Removing non-essential elements from the page makes the vital content easier to reach when in a hurry.

The last battery-drainer we will talk about is JavaScript. We already mentioned that download large libraries and frameworks is bad enough, but the actual parsing and execution of JS block can also lead to unnecessary spending.

JavaScript animations that cause constant redrawing of elements on the screen, listening for notifications form the server, and multiple AJAX requests can all drain the battery just a tiny bit, but it quickly adds up. According to this study, the JavaScript code consumes ~7% of Yahoo’s total rendering energy, ~17% on Amazon, and more than 20% on YouTube.

App With Powersaving Mode

We have showcased some of the above concepts in a simple demo app. It consists of a static website which reacts to the amount of battery left. When it gets below 30% the app goes into PowerSaving and turns darker, stops all animations, and removes all ads.

For demonstration purposes our app works with a virtual battery to enable quick toggling between fully charged and almost dead. It does not contain much code for working with the Battery API itself.

Our Demo App

Our Demo App

You can get the full code for the demo from the Download button near the top of the article. It’s written in Vue.js for easier data monitoring and has lots of comments to guide you through all that is happening.

Further Reading

If you want to find out more about the Battery Status API or about ways your precious battery is running down the drain, check out these excellent resources:

Battery Status API on MDN – here
The BatteryManager interface on MDN – here
5 Ways to Improve Battery Life in Your App – here
Who Killed My Battery: Analyzing Mobile Browser Energy Consumption – here

Building Your First App With Vue.js

building-your-first-app-with-vuejs

Today we’re going to exercise our Vue.js skills by building a simple app for browsing reddit posts. We’re going to construct the whole thing from scratch to demonstrate just how easy it is to create user interfaces with a framework like Vue.

This tutorial requires you to have at least some basic knowledge of JavaScript and Vue.js. If you aren’t familiar with Vue.js at all, we advise you to go and check out our article 5 Practical Examples For Learning Vue.js, where we show many of the core concepts with practical code snippets.

The App

What we want from our application is simply to fetch the feed from a number of subbreddits and display them. Here is what the end result will look like:

vue-app

Our Vue.js App

We will have six separate subreddit feeds showing five posts each. The posts have links to the content and discussion on reddit, as well as some other details. For the sake of simplicity we have omitted features such as adding/removing subreddits and doing searches, but they can be easily added on top of the existing app.

Setting Up The Workspace

You can download the full source code for the reddit browser app from the Download button near the top of the article. Before we actually look at the code, let’s make sure that everything is setup properly. Here is an overview of the file structure:

Our project's folder

Our project’s folder

As you can see it’s quite basic: we just have one HTML file, one CSS file, a script.js containing our JavaScript code. We’ve also added local copies of the Vue.js and Vue-resource libraries, but you can use a CDN if you prefer.

Thankfully, Vue.js doesn’t require any special configuration, so it should work straight out of the box. To start the app we just have to create a global Vue instance:

new Vue({
    el: 'body'
});

The only thing left to do now is start a local web server to enable cross-origin AJAX requests to the reddit API. The easiest way to do this on OS X/Ubuntu is by running the following command from the project’s directory:

python -m SimpleHTTPServer 8080

If everything is done properly our project should be available at localhost:8080.

Creating Custom Components

Our app is going to need two reusable components – one for the Posts, and another for Subreddits. The two components will be in a Child-Parent relationship, meaning that the Subreddit component will have multiple Posts nested in it.

Components Structure

Components Hierarchy

Let’s start with the Subreddit component, and more specifically it’s JavaScript:

// Parent | Subreddit component containing a list of 'post' components. 
var subreddit = Vue.component('subreddit',{
    template: '#subreddit',
    props: ['name'],

    data: function () {
        return { posts: [] }
    },

    created: function(){
        this.$http.get("https://www.reddit.com/r/"+ this.name +"/top.json?limit=5")
        .then(function(resp){
            this.posts=resp.data.data.children;
        });
    }
});

Here we define the new component under the name subreddit. In props we provide an array with all the parameters our component can receive – in this case it is just the name of the subbreddit we want to browse. Now if we want to add a subreddit block to the HTML we will use this markup:

<subreddit name="food"></subreddit>

The data property defines what variables are needed for each instance of the component and their default values. We will start with an empty posts array, and populate it in the created method. When a <subreddit> tag is created, Vue will take its name property, make a call to the reddit API to fetch the top 5 posts from the subreddit with that name, and save them in this.posts. For the HTTP requests we’ve used the vue-resource library instead of jQuery, since it is way tinier and automatically binds the correct context for this.

After we’ve acquired everything we need in the model, Vue.js will automatically render our Subreddit components. The actual view that the user sees is defined in a template in index.html:

<template id="subreddit">

    <div class="subreddit">
        <h2>{{ name | uppercase }}</h2>

        <ul class="item-list">
            <li v-for="obj in posts">
                <post :item="obj"></post>
            </li>
        </ul>
    </div>

</template>

Personally, I like to wrap all the elements of a component in a div container. This makes them easier to style and also seems more semantic (to me at least). Inside that container we have a title (the uppercase filter comes built-in with Vue) and an unordered list iterating over the elements returned from the reddit API call.

If you look closely at the HTML, you’ll also notice we are using a <post> tag. This isn’t some new fancy HTML element – it’s our child component!

// Child | Componenet represiting a single post.
var post = Vue.component('post', {
    template: "#post",
    props: ['item']
});

Post components will expect a object called item containing all of the information about a single post on reddit – things like title, URLs, number of comments, etc. As we saw earlier, this is done in a v-for loop inside the Subreddit (parent) component:

<li v-for="obj in posts">
    <post :item="obj"></post>
</li>

The colon prefixing :item="obj" is very important. It tells Vue that we are proving a JavaScript object called obj (as opposed to the string "obj"), allowing us to pass the data from the v-for.

Now that we have all the needed properties for a post, we can display them. The template looks scary at first, but really isn’t:

<template id="post">

    <div class="post">
        <a   :href="item.data.url" :style="item.data.thumbnail | setAsBackground" 
             target="_blank" class="thumbnail"></a>

        <div class="details">
            <a :href="item.data.url" :title="item.data.title" target="_blank" class="title">
                {{ item.data.title | truncate}}
            </a>          
            
            <div class="action-buttons">
                <a href="http://reddit.com{{ item.data.permalink }}" title="Vote">
                    <i class="material-icons">thumbs_up_down</i>
                    {{item.data.score}}
                </a>

                <a href="http://reddit.com{{ item.data.permalink }}" title="Go to discussion">
                    <i class="material-icons">forum</i>
                    {{item.data.num_comments}}
                </a>
            </div>
        </div>
    </div>
    
</template>

The only thing worth mentioning here is the use of the setAsBackground and truncate filters. In contrast to the uppercase filter we used earlier, they don’t come bundled with Vue and we had to make them ourselves.

Creating Custom Filters

Defining filters is quite easy. The Vue.filter() method provides us with the incoming string data, which we can transform whatever way we want and then simply return.

The first filter we’ll need takes the preview image for a post and creates a CSS rule setting it as background. We use this to set the inline styles with less effort.

It takes one parameter: the URL for the image. If that’s not available a placeholder image is shown instead.

// Filter that takes an image url and creates a CSS style.
Vue.filter('setAsBackground', function(value) {
    if(value && value!='self' && value!='nsfw') {
        return 'background-image: url(' + value + ')';  
    }
    else {
        return 'background-image: url(assets/img/placeholder.png)';   
    }
});

Our other filter takes strings and truncates them if they are too long. This is applied to the post titles, which often are way too lengthy for the design we had in mind.

// Filter for cutting off strings that are too long.
Vue.filter('truncate', function(value) {
    var length = 60;

    if(value.length <= length) {
        return value;
    }
    else {
        return value.substring(0, length) + '...';            
    }
});

The Full Code

Below we’ve listed all of the files for the app, so that you can look through the full code and get a better idea how the whole thing works.

/*-----------------
    Components 
-----------------*/

// Parent | Subreddit component containing a list of 'post' components. 
var subreddit = Vue.component('subreddit',{
    template: '#subreddit',
    props: ['name'],

    data: function () {
        return { posts: [] }
    },

    created: function(){
        this.$http.get("https://www.reddit.com/r/"+ this.name +"/top.json?limit=3")
        .then(function(resp){
            this.posts=resp.data.data.children;
        });
    }
});


// Child | Componenet represiting a single post.
var post = Vue.component('post', {
    template: "#post",
    props: ['item']
});


/*-----------------
   Custom filters 
-----------------*/

// Filter for cutting off strings that are too long.
Vue.filter('truncate', function(value) {
    var length = 60;

    if(value.length <= length) {
        return value;
    }
    else {
        return value.substring(0, length) + '...';            
    }
});


// Filter that takes an image url and creates a CSS style.
Vue.filter('setAsBackground', function(value) {
    if(value && value!='self' && value!='nsfw') {
        return 'background-image: url(' + value + ')';  
    }
    else {
        return 'background-image: url(assets/img/placeholder.png)';   
    }
});


/*-----------------
   Initialize app 
-----------------*/

new Vue({
    el: 'body'
});
<!DOCTYPE html>
<html>
<head>
    <title>Your First App With Vue.js</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="assets/css/styles.css">
</head>
<body>
    <div class="container">
        <subreddit name="aww"></subreddit>
        <subreddit name="space"></subreddit>
        <subreddit name="gifs"></subreddit>
        <subreddit name="food"></subreddit>
        <subreddit name="comics"></subreddit>
        <subreddit name="sports"></subreddit>
    </div>


    <template id="subreddit">
        <div class="subreddit">
            <h2>{{ name | uppercase }}</h2>
            <ul class="item-list">
                <li v-for="obj in posts">
                    <post :item="obj"></post>
                </li>
            </ul>
        </div>
    </template>

    <template id="post">
        <div class="post">
            <a   :href="item.data.url" :style="item.data.thumbnail | setAsBackground" 
                 target="_blank" class="thumbnail"></a>
            <div class="details">
                <a :href="item.data.url" :title="item.data.title" target="_blank" class="title">
                    {{ item.data.title | truncate}}
                </a>          
                
                <div class="action-buttons">
                    <a href="http://reddit.com{{ item.data.permalink }}" title="Vote">
                        <i class="material-icons">thumbs_up_down</i>
                        {{item.data.score}}
                    </a>

                    <a href="http://reddit.com{{ item.data.permalink }}" title="Go to discussion">
                        <i class="material-icons">forum</i>
                        {{item.data.num_comments}}
                    </a>
                </div>
            </div>
        </div>       
    </template>

    <script src="assets/js/vue.js"></script>
    <script src="assets/js/vue-resource.min.js"></script>
    <script src="assets/js/script.js"></script>
</body>
</html>
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

a{
    text-decoration: none;
}

a:hover{
    text-decoration: underline;
}

html{
    font: normal 16px sans-serif;
    color: #333;
    background-color: #f9f9f9;
}

.container{
    padding: 27px 20px;
    margin: 30px auto 50px;
    max-width: 1250px;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    background-color: #fff;
    box-shadow: 0 0 1px #ccc;
}

/* Subreddit component */

.subreddit{
    flex: 0 0 33%;
    min-width: 400px;
    padding: 20px 42px;
}

.subreddit h2{
    font-size: 18px;
    margin-bottom: 10px;
}

.subreddit .item-list{
    border-top: 1px solid #bec9d0;
    padding-top: 20px;
    list-style: none;
}

.subreddit .item-list li{
    margin-bottom: 17px;
}

/* Post component */

.post{
    display: flex;
}

.post .thumbnail{
    display: block;
    flex: 0 0 60px;
    height: 60px;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    margin-right: 10px;
    border-radius: 4px;
    margin-right: 12px;
}

.post .details{
    display: flex;
    flex-direction: column;
}

.post .details .title{
    font-size: 15px;
    margin-bottom: 3px;
    color: #04477b;
}

.post .details .title:visited{
    color: purple;
}

.post .details .action-buttons a{
    font-size: 11px;
    margin-right: 4px;
    display: inline-block;
    color: #666;
}

.post .details .action-buttons i{
    font-size: 10px;
    margin-right: 1px;
}

@media(max-width: 1250px){

    .container{
        justify-content: center;
        margin: 30px 30px 50px 30px;
    }
}

@media(max-width: 500px){

    .subreddit{
        min-width: 300px;
        padding: 20px 15px;
    }
}

Note that after creating our two components, the entire app interface comes down to:

<div class="container">
    <subreddit name="aww"></subreddit>
    <subreddit name="space"></subreddit>
    <subreddit name="gifs"></subreddit>
    <subreddit name="food"></subreddit>
    <subreddit name="comics"></subreddit>
    <subreddit name="sports"></subreddit>
</div>

The JavaScript file isn’t too large either and this is one of my favorite things about Vue. It does so much of the work for us that in the end we are left with a very clean and comprehensive piece of code.

Further Reading

The main focus of this tutorial was to show the process of building a simple Vue.js app. To keep it short we haven’t stopped to explain every tiny syntax peculiarity, but worry not! There are many awesome resources where you can learn the basics:

  • The official Vue.js starting guide and docs – here.
  • Excellent video series from Laracasts – here.
  • Our very own article: 5 Practical Examples For Learning Vue.js – here.

This concludes our Vue.js tutorial! We hope that you’ve had lots of fun with it and that you’ve learned a thing or two. If you have any suggestions or questions, feel free to leave a message in the comment section below :)

Powered by Gewgley