Slides on “Responsive Web”

Tonight, I presented on the topic of “Responsive Web” to our local WordPress meetup group based in Cincinnati. Below is the abstract and slides:

What is responsive web design, and why should you care? As a developer or when otherwise modifying an existing theme, how do you go about making elements on a page responsive? At December’s meeting, we’ll seek to answer these questions and others.

Some items that we’ll expect to cover include:

  • Motivations behind responsive web design
  • Tools for testing your a theme or page in a variety of contexts
  • Building your site to work on all types of displays, be it desktop, phone, tablet, or other

Capturing a Paste Event in Firefox

Imagine yourself building a web app that supports image uploads by simply capturing your computer’s paste keyboard shortcut. Sites like imgur.com already support this behavior and it adds an extra level of convenience for what would otherwise require several additional button clicks.

The paste event has been supported in major browsers for some time now, but interestingly Firefox doesn’t trigger the event unless the user’s cursor is currently focussed in an editable region. This seems to be contradictory to the specification itself, which states:

The paste event has no default action in a non-editable context, but the event fires regardless.

There is an open ticket on Bugzilla related to this behavior, while others have resorted to tricks to move the user’s cursor when detecting that a combination of the “control” and “v” keys are pressed.

In my testing, however, I found that the mere presence of a contenteditable element at the root of the page was sufficient in allowing the paste event to trigger normally in Firefox, even if the element is hidden using display: none; styles.

Check it out in the demo below:

In Firefox (v40.0.3 as of today), you’ll find that the paste event is captured so long as the contenteditable attribute is assigned. When toggling the contenteditable off, thereby removing the attribute, the paste is no longer captured. Admittedly, it’s a nuisance to include this unused element, but it’s certainly an improvement over redirecting or otherwise faking the paste interaction using one of the aforementioned tricks.

While there are many other browser inconsistencies to account for when working with the paste event, I was surprised to find that despite what I had read, it was easier than I had anticipated to capture page-wide pastes in Firefox.

Node.js Testing Doesn’t Need to be Difficult

Whenever testing code in the context of Node.js comes up in conversation, it’s very likely to be accompanied by an exploration of testing-related tooling and strategies. If you’ve ever participated in such a discussion, it’s not uncommon to hear one or more of the following terms thrown around: test runners, assertion libraries, mocking, stubbing, spying, and continuous integration. While these can certainly be valuable tools to consider as you build out a full test suite, the thought of familiarizing oneself with each can be intimidating to a developer new to the Node.js ecosystem or testing in general.

But you don’t need any of these tools to write a basic test. The idea behind a test is to make an assumption about your code that you’d expect to be true. In cases where these expectations are in fact untrue, the script should abort with a failing exit code. Typically the script is terminated by an uncaught error.

Let’s imagine we have a simple sum function which accepts two arguments where the return value is the sum of the two arguments.

function sum( x, y ) {
    return x + y;
}

Our most basic test might simply consider a summing of two arbitrary numbers for which we’d expect the assumed value to be returned.

var calculated = sum( 1, 2 );

if ( calculated !== 3 ) {
    throw new Error( ‘3 was expected, but we saw ‘ + calculated );
}

If we were to run this script, we’d see that it quietly finishes because the summed value is equal to our expected assumption. If we were to tamper with our original sum function by, say, changing the + operator to -, we’d see that it terminates with an uncaught error using the message we specified.

That’s really all there is to basic testing. We could optionally choose to add this as a script in our project’s package.json so that it could be predictably run through the npm test command.

As you continue to build more tests, you’ll want to consider the topics listed in the first paragraph, as they’ll help to extend and simplify common usage patterns. A brief description of each is included below:

  • Assertion libraries define a syntax for declaring your assumptions. Node includes a core module (assert) which provides a limited assertion syntax.
  • Test runners provide a means to combine your tests into a logical grouping, and often provide a reporting interface to more easily interpret the results of your tests.
  • Mocking, stubbing, and spying are related topics which allow you to create simulated functions which can then be observed, granting you insight into how your code is executed without the need to actually execute it.
  • Continuous integration is a service which will automatically run tasks (including tests) when the code is changed.

Running Grunt tasks without grunt-cli

Grunt is a JavaScript task runner that helps automate common development tasks such as minifying and testing code. Traditionally, using Grunt is a two-step process. First you install Grunt’s command-line interface (GLI) globally using npm (npm install -g grunt-cli). Next, in any project where Grunt is to be used, you must locally install Grunt and any Grunt plugins you wish to include. The command-line interface is installed separately so as to allow multiple versions of Grunt to be installed on the same machine, and acts as a simple wrapper for executing tasks through the local Grunt module.

Once you’ve installed Grunt both locally and globally, and after configuring your Gruntfile, running a task is as simple as passing a task name to Grunt’s command-line interface. For example, if my Gruntfile defines a test task, I could run it by entering grunt test into my command-line. It may sometimes be necessary or handy to be able to trigger a task without the Grunt CLI, in which case you can use the following code snippet, replacing test with the task(s) of your choosing:

node -e "require('grunt').tasks(['test']);

There are a few common scenarios in which using the above syntax would be preferable to running tasks through Grunt directly. Specifically, this may be a requirement in cases where another developer or system does not have Grunt installed globally.

As an illustration, consider the scripts feature of npm. Often overlooked, the scripts section of a package.json allows a developer to define command-line scripts to be run via predefined keywords. For example, you could trigger a Grunt test task with the following configuration, to be called using npm test.

{
    "name": "my-package",
    "version": "1.0.0",
    "scripts": {
        "test": "node -e \"require('grunt').tasks(['test']);\""
    }
}

The advantage of using the command above in place of grunt test is that your test script can now be executed regardless of whether the global Grunt module is installed.

Removing global dependencies may also help in the case of continuous integration services. Personally, I use Travis CI to run unit tests against my GitHub project commits. Up until recently, I’ve used the before_script section of my .travis.yml configurations to install the global Grunt module. By running my tasks directly, I can safely omit this configuration section. Other services may not even grant you the option to configure global dependencies, so this may be your only option.

And it’s not just Grunt, either. Where possible, make an effort to eliminate the need for global dependencies in your npm scripts. Not every developer is going to be familiar with the tools that you choose to use, and removing such dependencies helps to avoid frustration caused by error messages. It may someday be possible to define global dependencies in a project’s package.json, but that’s not likely to be coming anytime soon.