Author Archive

It’s Not All Doom And Gloom On The Web // Opinion Column


  

In this article I’d like to discuss the changes happening on the Web and argue that its future is not as problematic and endangered as a lot of people make it out to be. The article is based on the talk I’ve presented at the Smashing Conference a couple of days ago, and you can also see the slides and watch the screencast (see below).

I have been developing websites professionally for the greater part of the last 15 years, and written quite a few books and a lot of articles. Yet when I look around right now, I do feel incredibly… stupid and wonder if I should hang up my coat and do something else. Almost daily we see new tools, new best practices and systems to use, and a lot of them are very far removed from the original Web development technologies that are defined by the standards bodies.

I am also very confused about the message of doom and gloom we have right now about the Web. Many spokespeople look at the sales numbers of smartphones and call us out for not doing enough to keep developers interested and to get newcomers to start on the Web rather than somewhere else.

To me, not everything is doom and gloom, and the Web isn’t losing. I am actually very excited about what we are doing on the Web, and I see a lot of great things happening right now. I look back at what I had to work with in the past and see how professional and rich our development environments are now, and I am very happy indeed.

So, what happened? How come I am excited about the Web and its immediate future, while others feel an urge to protect it from certain doom?

Ubiquity And Speed Of Innovation

Two of the problems we see right now are ubiquity of technology and connectivity. The Web is not the cool new thing any longer. Instead, everybody seems to be on it and using it all the time — not like we did in the past with a desktop computer and browser, but through apps and short updates in social media.

Another issue is the speed of innovation. Almost monthly, something exciting comes out that makes the last big new thing seem boring and unwieldy.

One thing is that we perceive constant new demands from our end users and people who spend money on marketing in our environment. A lot of this is just people repeating things they’ve heard — suspension of disbelief. A video is going around of people being handed an iPhone 4S and being told it is the new iPhone 5. What did they think of it? Nearly all of them found something extraordinarily cool about it and said it is better than the last one — even people who owned the same version!

Of course, in getting excited about these cool new things, a lot of us say that everyone these days has these cool devices and new technologies and that we need to keep up. As Thomas Fuchs puts it:

“Let’s put this in context: mobile Internet usage has doubled last year, and right now about 20% of all Web traffic in the US is from mobile devices. This means Retina screens will soon become the norm.”

It is a very myopic way of thinking, though. The Web is worldwide, and first of all, only a few of us can afford these devices. Secondly, a big chunk of us cannot get these devices where we live. Thirdly, people forget that when the initial investment hurts, people don’t want to replace their hardware a few months after. Not to mention that high-speed connectivity is not as ubiquitous as we consider it to be, especially for mobile devices.

Don’t Always Trust The Web

We seem to be not quite at ease with what we are doing at the moment, despite the fact that our job market being ridiculously good and that we get paid very well for a relatively easy job. Instead of considering this, we constantly measure ourselves against success stories that have been inflated by tech blogs that exist to inflate such stories. Or, as Steve Furtick puts it:

“The reason we struggle with insecurity is because we compare our behind-the-scenes with everyone else’s highlight reel.”

At TechCrunch Disrupt, Mark Zuckerberg was interviewed, and in the immediate coverage by the tech press, he was quoted as saying that HTML5 was a big mistake. The quote that was repeated all over went like this:

“I think that the biggest mistake that we made as a company was betting too much on HTML5 as opposed to native, because it just wasn’t there.”

The interesting bits, though, were ignored in the coverage:

“It’s not that HTML5 is bad. I’m actually long-term really excited about it. And one of the things that’s interesting is we have actually more people on a daily basis using mobile Web Facebook than we have using our iOS or Android apps combined. So, mobile Web is a big thing for us.”

It seems that the main failure was Facebook’s approach to and internal system for creating HTML5 apps — not the technology itself:

“But there’s no doubt that we went for this approach, we built this internal framework that we called Faceweb, which was basically this idea that we can take the infrastructure that we built out for pushing code everyday, not having to submit to an app store, building Web code on the Web stack that we have, and that we can translate that into mobile development. We just were never able to get the quality of it we wanted…”

It seems to me that the path to keeping your sanity in this world of ours is not to care about the shouting news outlets that need clicks to make money.

It’s All About Hardware

A few things are going wrong right now, and most of them are related to the fact that we emulate native apps and the practices of thick client development, rather than embracing the fact that the Web is a different challenge. Yes, it is software. No, it is not a defined platform with established processes.

There is a secret behind all of the failures of HTML5 in the mobile market, and it is actually very annoying: it is all about the hardware.

We can innovate HTML5 until we are blue in the face, and we can optimize browser performance to reach rocket ship-level speed, but if the hardware and operating system providers don’t allow us to be on their hardware or give early access, then there is no way browsers can perform as well as native code.

When you think about it, most of the money on mobile devices comes from app sales. And the Web gets in the way of app sales as they exist now. So, there is not really much incentive to make Web apps perform well or access all of the good parts in the hardware because then developers wouldn’t have to become part of a vendor’s program or pay to get access to their sales platform.

The lack of drivers that would enable apps written in Web technologies to access the whole hardware is the biggest issue. This even affects laptops and desktop machines — a lot of WebGL cannot even be used in brand new computers.

Firefox OS
Firefox OS will be the first truly open operating system for mobile devices. Image credit.

Mozilla is going full force right now to change this dilemma. Firefox OS will be the first truly open operating system for mobile devices. Underneath the hood of Firefox OS are the Web APIs: open-source drivers with JavaScript interfaces to access all of the hardware of the phone.

The Beautiful Side Of The Web

But that is by the by. Let’s go back and see why the Web actually is a great idea for us to work with. Mozilla Webmaker (which I’m involved in) is an ongoing project to turn pure consumers of the Web into makers. We teach basic Web editing skills, how to publish and mix video with online content, and basic ways to keep safe and have a good time on the Web. Attending one of these events is not only humbling but incredible. It is amazing to see how things that we consider boring and “common knowledge� make people go crazy for creating and doing things they haven’t done before.

A lot of the frustration we see stems from people — us included — having forgotten the main principles that the Web is based on. I am right now reading New Model Army by Adam Roberts, a science-fiction book about a war in England between the traditional army and the New Model Army, a group of mercenaries organized via the Web and wikis.

Mozilla Webmaker
Mozilla Webmaker is an ongoing project to turn pure consumers of the Web into makers.

The main difference of the NMA is that there is no hierarchy — everything is voted upon and decided on the spot. They are almost impossible to defeat because they move much faster as a result of not having to wait for orders from above. They are also professional soldiers, there to fight other soldiers without any ideological or national interference. All they defend is the right to be truly democratic in their decision-making.

The Starfish and the Spider is another book that talks about a principle that makes the Web what it is. It explains that organizations without a single point of failure are much more likely to succeed than those that have a massive hierarchy and are likely to be crushed by their own size. App markets are those things.

One of the main issues, though, is that the Web exists, driven by the open and free technologies that we advocate. It works, and it has outlived many of the other closed technologies that were always heralded as its end. But this is not at all a time to sit on our laurels. There is no doubt that the Web has lost a lot of its appeal to new people.

Partly this is because we’ve become mainstream. The Web is not the edgy cool new technology that we can play with any way we want. We have to consider that mainstream media is powering a lot of it with advertising and cross-promotion of real-world events and products. Thus, we should be in the productive phase of the hype cycle, but somehow we’ve missed this point and are still struggling to find a way to turn over a lot of products without reinventing.

One big challenge is to rethink the tools we have. As Lea Verou put it:

“I often think that command-line editors like Vi and Emacs were made by sadists who enjoy making people feel stupid, frustrated and helpless.”

Whenever we talk about the Web, sooner or later the talk is about text editors and writing a lot of code by hand. We should be better than that by now, and we should make it easier for anyone to create on the Web.

The success of other platforms with new developers is that they are simple to learn. You are in a fixed environment, you get a few Lego bricks to play with, and you can build your first thing.

Old Tales Of The Web

Instead of concentrating on being as fast on our feet, we keep boring people with the same old tales of how the Web came around and how HTML got better and we can add semantic value with microformats, and many other tales of yesterday. In a world where all browsers run the same incredibly forgiving parser, talk about the purity of HTML and semantics falls on the deaf ears of those just starting out, and it is actually a deterrent. We’ve failed to make semantics matter — microformats are a great idea, but when no browser does anything visible or useful with them and they don’t bring any benefit with search engines, then they are superfluous to people who just want to publish their work.

When HTML5 got defined, we should have been quicker to get our needs and demands in. If you think about it, the JavaScript part of HTML5 is incredibly powerful, but the semantics are not that amazing. We knew we wanted to move the Web from text to apps, but we failed to define the necessary widgets. Instead we got elements that were defined as a result of analyzing which classes people used in their HTML in the past. Most app-style widgets were created with JavaScript and had no classes at all because not many libraries enhanced progressively. Even now, we don’t help browser makers or demand better support for rich forms.

We’ve even failed to think about a packaged format for an HTML5 app. Portability means that we have installers and de-installers, instead of running an app in a browser. Right now we have no one-size-fits-all approach to that. The W3C widgets were not the right format, so Chrome, Mozilla and PhoneGap all came up with their own formats instead.

Outside the world of those who want to crack the app issue, we flee into a world of abstractions. We build preprocessors for CSS, and we build JavaScript libraries that make it easier but that also completely replace the syntax of JavaScript with their own, and we applaud all of these efforts and call them solutions without knowing whether they’ll be supported in future. A lot of them have come and gone without leaving much of a footprint. Maybe it is time to recognize that moving up levels in a building doesn’t mean that the water damage on the ground floor won’t be an issue sooner or later.

We’re always so proud of the portability of our Web technologies and that they are so easy to learn and use and that they adapt to whatever you throw at them. But when you look at it from an outsider’s point of view, a lot of what we do is not portable or reusable. Every single HTML slide system is a great example of that. Or try sending an HTML file to someone who doesn’t know HTML — they’ll open it in Word and everything will break.

And we don’t help the cause much. We repeat the mistake we’ve make in the past of building solutions for one browser or demanding that the end user turn on things and change their setup. Many open solutions demand that the user takes five steps where one installer would be the right thing, and we expect people to like setting up a lot of tools using the command line. This doesn’t help us win against closed technology.

Getting Out Of The Comfort Zone

How do we win back the hearts and minds of developers? I think we need to create new products and take different approaches than we have in the past. We should focus on making things easier for people, not striving for purity and delivering like we have in the past. We need to leave our comfort zone, because that is when the magic happens.

Bret Victor’s “Inventing on Principle� talk is a great start for this. Bret’s big principle is to enable creation by making the step from creating to seeing the results as short as possible. He shows off a few tools that not only are WYSIWYG but that work in both directions. You write a game by playing and adjusting the position of the player along a timeline. Thus, your testing happens while you develop.


Bret Victor on “Inventing on Principle“.

In-browser developer tools are a step towards a world like that. We create and change in the browser, rather than having to reload every time we change our code. Live reloading with editors works the same way. Having this immediacy where it happens makes a lot of sense, and we should be more vocal that every browser these days is also a creation tool.

Other big things I am very excited about are Web Components (which define the missing app widgets we need), X-Tag (which makes those available cross-browser) and the Mortar and WebGame stub systems. The last two enable any developer to start an app or game from building blocks and with a deployment script that uses GitHub as the host. You even create it offline, and the app will manifest for you. Watch out for this.

Another way to get people to think about the Web is not to make them think about it, but instead to use what they do already. Bananabread is a game demo written in C++ that uses Emscripten to run in JavaScript and WebGL. This is recycling as opposed to creating something new that might not be used.

In general, I think we need tools much, much more. A very interesting move was made by Adobe — yes, the Adobe behind the evil Flash — which released Brackets, an editor that also ties into live rendering in the browser. It is pretty alpha, but the very important point here is that the company that makes all of its money from tools and that rules the graphics-creation market supreme is playing with open source. It wants developers to work with it and make Brackets better and see how it works for them. This is a good chance to work with a company that knows how to build good tools and get it to open up more.

“Today is the tomorrow you expected yesterday.”

The Lost Thing by Shaun Tan.

All in all, there is no doom and gloom here. But we should have a sense of urgency. We have an incredible amount of good things to share and talk about, but if we fail to do so, we’ll look like an outdated group of experts. Today is the day you can help the Web be sexy again.

Step One: Write And Share

The first step is simple. Write and share your wisdom. Do not do it in random places. Instead, join one of the open systems that already contain great content and make it better:

Step Two: Complain In Right Channels

The second step is to reconsider our ways of complaining. Yes, venting on Twitter or on our blogs when things are broken feels good, but that doesn’t give you the feedback you need. And you’re not reaching the people who can fix your problems. You’re merely advertising that the Web is not ready and that people are not even fixing the problems. And that is not true; when you file bugs and complain on channels where browser developers and standards makers are available, things do change for the better.

One thing that has come about in recent years is a massive amount of collaborative development tools that enable you to host a development issue and get it fixed by others with you directly there. Use them, because having an issue fixed with an immediate result works much faster than long-winded explanations about what can be done:

Step Three: Support Open Training Tools

Last but not least, support the open training and education tools that are mushrooming all over the Web right now. A lot of them are funded and need content. The earlier we get people to play with the Web, the harder it will be for them to get messed up again by traditional education. I’d wager that all of us came to the Web because of our interest and from tinkering with things, rather than by graduating from a course. Let’s make that the way in for new makers of the Web, too:

The Future Is Bright!

When you try to get people excited about the Web, remember to point out the good things about it. We are far too good at complaining openly about things that are broken, while failing to share our excitement. Let’s do that more.

Finding Nemo was never advertised like this:

“A movie in which a wife and all but one child in a family are brutally murdered. The last child gets kidnapped, and the father undertakes a desperate search to find it, his only ally being a mentally challenged woman.”

(al) (vf)


© Christian Heilmann for Smashing Magazine, 2012.


JavaScript Events And Responding To The User // Back To Basics


  

Whenever people ask me about the most powerful things in JavaScript and the DOM, I quickly arrive at events. The reason is that events in browsers are incredibly useful. Furthermore, decoupling functionality from events is a powerful idea, which is why Node.js became such a hot topic.

Today, let’s get back to the basics of events and get you in the mood to start playing with them, beyond applying click handlers to everything or breaking the Web with <a href="javascript:void(0)"> links or messing up our HTML with onclick="foo()" inline handlers (I explained in detail in 2005 why these are bad ideas).

Note: This article uses plain JavaScript and not any libraries. A lot of what we’ll talk about here is easier to achieve in jQuery, YUI or Dojo, but understanding the basics is important because you will find yourself in situations where you cannot use a library but should still be able to deliver an amazing solution.

Disclaimer: The event syntax we’ll be using here is addEventListener(), as defined in the “DOM Level 3 Events� specification, which works in all browsers in use now except for Internet Explorer below version 9. A lot of the things we’ll show can be achieved with jQuery, though, which also supports legacy browsers. Come to think of it, one simple addEventListener() on DOMContentLoaded is a great way to make sure your script does not run on legacy browsers. This is a good thing. If we want the Web to evolve, we need to stop giving complex and demanding code to old browsers. If you build your solutions the right way, then IE 6 would not need any JavaScript to display a workable, albeit simpler, solution. Think of your product as an escalator: if your JavaScript does not execute, the website should still be usable as stairs.

Before we get into the details of events and how to use them, check out a few demos that use scroll events in a clever way to achieve pretty sweet results:

All of this is based on event handling and reading out what the browser gives us. Now, let’s look at repeating the basics of that.

Basics: What Is An Event?

var log = document.getElementById('log'),
    i = null, 
    out = [];
for (var i in window) {
  if ( /^on/.test(i)) { out[out.length] = i; }
}
log.innerHTML = out.join(', ');

In my case, running Firefox, I get this:

onmouseenter, onmouseleave, onafterprint, onbeforeprint, onbeforeunload, onhashchange, onmessage, onoffline, ononline, onpopstate, onpagehide, onpageshow, onresize, onunload, ondevicemotion, ondeviceorientation, onabort, onblur, oncanplay, oncanplaythrough, onchange, onclick, oncontextmenu, ondblclick, ondrag, ondragend, ondragenter, ondragleave, ondragover, ondragstart, ondrop, ondurationchange, onemptied, onended, onerror, onfocus, oninput, oninvalid, onkeydown, onkeypress, onkeyup, onload, onloadeddata, onloadedmetadata, onloadstart, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onmozfullscreenchange, onmozfullscreenerror, onpause, onplay, onplaying, onprogress, onratechange, onreset, onscroll, onseeked, onseeking, onselect, onshow, onstalled, onsubmit, onsuspend, ontimeupdate, onvolumechange, onwaiting, oncopy, oncut, onpaste, onbeforescriptexecute, onafterscriptexecute

That is a lot to play with, and the way to do that is by using addEventListener():

element.addEventListener(event, handler, useCapture);

For example:

var a = document.querySelector('a'); // grab the first link in the document
a.addEventListener('click', ajaxloader, false);

The element is the element that we apply the handler to; as in, “Hey you, link! Make sure you tell me when something happens to you.� The ajaxloader() function is the event listener; as in, “Hey you! Just stand there and keep your ears and eyes peeled in case something happens to the link.� Setting the useCapture to false means that we are content to capture the event on bubbling, rather than the capturing phase. This is a long and arduous topic, well explained on Dev.Opera. Let’s just say that by setting the useCapture to false, you will be fine in 99.7434% of cases (a rough approximation). The parameter is actually optional in all browsers but Opera.

Now, the event handler function gets an object as a parameter from the event, which is full of awesome properties that we can play with. If you try out my example, you’ll see what the following code does:

var log = document.getElementById('log'),
    i = 0,
    out = '';
    
document.addEventListener('click', logeventinfo, false);
document.addEventListener('keypress', logeventinfo, false);

function logeventinfo (ev) {
  log.innerHTML = '';
  out = '<ul>';
  for (var i in ev) {
    if (typeof ev[i] === 'function' || i === i.toUpperCase()) {
      continue;
    }
    out += '<li><span>'+i+'</span>: '+ev[i]+'</li>';
  }
  log.innerHTML += out + '</ul>';
}

You can assign several event handlers to the same event, or the same handler to various events (as shown in this demo).

The ev is what we get back from the event. And (again, in my case, in Firefox) a lot of interesting things are in it:

originalTarget: [object HTMLHtmlElement]
type: click
target: [object HTMLHtmlElement]
currentTarget: [object HTMLDocument]
eventPhase: 3
bubbles: true
cancelable: true
timeStamp: 574553210
defaultPrevented: false
which: 1
rangeParent: [object Text]
rangeOffset: 23
pageX: 182
pageY: 111
isChar: false
screenX: 1016
screenY: 572
clientX: 182
clientY: 111
ctrlKey: false
shiftKey: false
altKey: false
metaKey: false
button: 0
relatedTarget: null
mozPressure: 0
mozInputSource: 1
view: [object Window]
detail: 1
layerX: 182
layerY: 111
cancelBubble: false
explicitOriginalTarget: [object HTMLHtmlElement]
isTrusted: true
originalTarget: [object HTMLHeadingElement]
type: click
target: [object HTMLHeadingElement]
currentTarget: [object HTMLDocument]
eventPhase: 3
bubbles: true
cancelable: true
timeStamp: 574554192
defaultPrevented: false
which: 1
rangeParent: [object Text]
rangeOffset: 0
pageX: 1
pageY: 18
isChar: false
screenX: 835
screenY: 479
clientX: 1
clientY: 18
ctrlKey: false
shiftKey: false
altKey: false
metaKey: false
button: 0
relatedTarget: null
mozPressure: 0
mozInputSource: 1
view: [object Window]
detail: 1
layerX: 1
layerY: 18
cancelBubble: false
explicitOriginalTarget: [object Text]
isTrusted: true

It also differs from event to event. Try clicking the demo and pressing keys, and you will see that you get different results. You can also refer to the full list of standard event properties.

The Last Of The Basics: Preventing Execution And Getting The Target

Two more things are important when it comes to events in the browser: we have to stop the browser from carrying out its default action for the event, and we have to find out which element the event fired on. The former is achieved with the ev.preventDefault() method, and the latter is stored in ev.target.

Say you want to know that a link has been clicked, but you don’t want the browser to follow it because you have a great idea of what to do with that event instead. You can do this by subscribing to the click event of the link, and you can stop the browser from following it by calling preventDefault(). Here is the HTML:

<a class="prevent" href="http://smashingmagazine.com">Smashing, my dear!</a>
<a class="normal" href="http://smashingmagazine.com">Smashing, my dear!</a>

And the JavaScript:

var normal = document.querySelector('.normal'),
    prevent = document.querySelector('.prevent');

prevent.addEventListener('click', function(ev) {
  alert('fabulous, really!');
  ev.preventDefault();
}, false);

normal.addEventListener('click', function(ev) {
  alert('fabulous, really!');
}, false);

Note: document.querySelector() is the standard way to get an element in the DOM. It is what the $() method in jQuery does. You can read the W3C’s specification for it and get some explanatory code snippets on the Mozilla Developer Network (MDN).

If you now click the link, you will get an alert. And when you hit the “OK� button, nothing more happens; the browser does not go to http://smashingmagazine.com. Without the preventDefault(), the browser will show the alert and follow the link. Try it out.

The normal way to access the element that was clicked or hovered over or that had a key pressed is to use the this keyword in the handler. This is short and sweet, but it’s actually limiting because addEventListener() gives us something better: the event target. It could also be confusing because this might already be bound to something else, so using ev.currentTarget as noted in the specification is a safer bet.

Event Delegation: It Rocks. Use It!

Using the target property of the event object, you can find out which element the event occurred on.

Events happen by going down the whole document tree to the element that you interacted with and back up to the main window. This means that if you add an event handler to an element, you will get all of the child elements for free. All you need to do is test the event target and respond accordingly. See my example of a list:

<ul id="resources">
  <li><a href="http://developer.mozilla.org">MDN</a></li>
  <li><a href="http://html5doctor.com">HTML5 Doctor</a></li>
  <li><a href="http://html5rocks.com">HTML5 Rocks</a></li>
  <li><a href="http://beta.theexpressiveweb.com/">Expressive Web</a></li>
  <li><a href="http://creativeJS.com/">CreativeJS</a></li>
</ul>

Hover your mouse over the list in this example and you will see that one event handler is enough to get the links, the list item and the list itself. All you need to do is compare the tagName of the event target to what you want to have.

var resources = document.querySelector('#resources'),
    log = document.querySelector('#log');

resources.addEventListener('mouseover', showtarget, false);

function showtarget(ev) {
  var target = ev.target;
  if (target.tagName === 'A') {
    log.innerHTML = 'A link, with the href:' + target.href;
  }
  if (target.tagName === 'LI') {
    log.innerHTML = 'A list item';
  }
  if (target.tagName === 'UL') {
    log.innerHTML = 'The list itself';
  }
}

This means you can save a lot of event handlers — each of which is expensive to the browser. Instead of applying an event handler to each link and responding that way — as most people would do in jQuery with $('a').click(...) (although jQuery’s on is OK) — you can assign a single event handler to the list itself and check which element was just clicked.

The main benefit of this is that you are independent of the HTML. If you add more links at a later stage, there is no need to assign new handlers; the event handler will know automatically that there is a new link to do things with.

Events For Detection, CSS Transitions For Smoothness

If you remember the list of properties earlier in this article, there is a lot of things we can use. In the past, we used events for simple hover effects, which now have been replaced with effects using the :hover and :focus CSS selectors. Some things, however, cannot be done with CSS yet; for example, finding the mouse’s position. With an event listener, this is pretty simple. First, we define an element to position, like a ball. The HTML:

<div class="plot"></div>

And the CSS:

.plot {
  position:absolute;
  background:rgb(175,50,50);
  width: 20px;
  height: 20px;
  border-radius: 20px;
  display: block;
  top:0;
  left:0;
}

We then assign a click handler to the document and position the ball at PageX and pageY. Notice that we need to subtract half the width of the ball in order to center it on the mouse pointer:

var plot = document.querySelector('.plot'),
    offset = plot.offsetWidth / 2;
document.addEventListener('click', function(ev) {
  plot.style.left = (ev.pageX - offset) + 'px';
  plot.style.top = (ev.pageY - offset) + 'px';
}, false);

Clicking anywhere on the screen will now move the ball there. However, it’s not smooth. If you enable the checkbox in the demo, you will see that the ball moves smoothly. We could animate this with a library, but browsers can do better these days. All we need to do is add a transition to the CSS, and then the browser will move the ball smoothly from one position to another. To achieve this, we define a new class named smooth and apply it to the plot when the checkbox in the document is clicked. The CSS:

.smooth {
  -webkit-transition: 0.5s;
     -moz-transition: 0.5s;
      -ms-transition: 0.5s;
       -o-transition: 0.5s;
          transition: 0.5s;
}

The JavaScript:

var cb = document.querySelector('input[type=checkbox]');
cb.addEventListener('click', function(ev) {
  plot.classList.toggle('smooth');
}, false);

The interplay between CSS and JavaScript events has always been powerful, but it got even better in newer browsers. As you might have guessed, CSS transitions and animations have their own events.

How Long Was A Key Pressed?

As you might have seen in the list of available events earlier, browsers also give us a chance to respond to keyboard entry and tell us when the user has pressed a key. Sadly, though, key handling in a browser is hard to do properly, as Jan Wolter explains in detail. However, as a simple example, let’s look how we can measure in milliseconds how long a user has pressed a button. See this keytime demo for an example. Press a key, and you will see the output field grow while the key is down. Once you release the key, you’ll see the number of milliseconds that you pressed it. The code is not hard at all:

var resources = document.querySelector('#resources'),
    log = document.querySelector('#log'),
    time = 0;

document.addEventListener('keydown', keydown, false);
document.addEventListener('keyup', keyup, false);

function keydown(ev) {
  if (time === 0) { 
    time = ev.timeStamp; 
    log.classList.add('animate');
  }
}
function keyup(ev) {
  if (time !== 0) {
    log.innerHTML = ev.timeStamp - time;
    time = 0;
    log.classList.remove('animate');
  }
}

We define the elements we want and set the time to 0. We then apply two event handlers to the document, one on keydown and one on keyup.

In the keydown handler, we check whether time is 0, and if it is, we set time to the timeStamp of the event. We assign a CSS class to the output element, which starts a CSS animation (see the CSS for how that is done).

The keyup handler checks whether time is still 0 (as keydown gets fired continuously while the key is pressed), and it calculates the difference in the time stamps if it isn’t. We set time back to 0 and remove the class to stop the animation.

Working With CSS Transitions (And Animations)

CSS transitions fire a single event that you can listen for in JavaScript called transitionend. The event object then has two properties: propertyName, which contains the property that was transitioned, and elapsedTime, which tells you how long it took.

Check out the demo to see it in action. The code is simple enough. Here is the CSS:

.plot {
  background:rgb(175,50,50);
  width: 20px;
  height: 20px;
  border-radius: 20px;
  display: block;
  -webkit-transition: 0.5s;
     -moz-transition: 0.5s;
      -ms-transition: 0.5s;
       -o-transition: 0.5s;
          transition: 0.5s;
}

.plot:hover {
  width: 50px;
  height: 50px;
  border-radius: 100px;
  background: blue;
}

And the JavaScript:

plot.addEventListener('transitionend', function(ev) {
  log.innerHTML += ev.propertyName + ':' + ev.elapsedTime + 's ';
}, false);

This, however, works only in Firefox right now because Chrome, Safari and Opera have vendor-prefixed events instead. As David Calhoun’s gist shows, you need to detect what the browser supports and define the event’s name that way.

CSS animation events work the same way, but you have three events instead of one: animationstart, animationend and animationiteration. MDN has a demo of it.

Speed, Distance And Angle

Detecting events happening is one thing. If you want to do something with them that is a beautiful and engaging, then you need to go further and put some math into it. So, let’s have a go at using a few mouse handlers to calculate the angle, distance and speed of movement when a user drags an element across the screen. Check out the demo first.

var plot = document.querySelector('.plot'),
    log = document.querySelector('output'),
    offset = plot.offsetWidth / 2,
    pressed = false,
    start = 0, x = 0, y = 0, end = 0, ex = 0, ey = 0, mx = 0, my = 0, 
    duration = 0, dist = 0, angle = 0;

document.addEventListener('mousedown', onmousedown, false);
document.addEventListener('mouseup', onmouseup, false);
document.addEventListener('mousemove', onmousemove, false);

function onmousedown(ev) {
  if (start === 0 && x === 0 && y === 0) {
    start = ev.timeStamp;
    x = ev.clientX;
    y = ev.clientY;
    moveplot(x, y);
    pressed = true;
  }
}
function onmouseup(ev) {
  end = ev.timeStamp;
  duration = end - start;
  ex = ev.clientX;
  ey = ev.clientY;
  mx = ex - x;
  my = ey - y;
  dist = Math.sqrt(mx * mx + my * my);
  start = x = y = 0;
  pressed = false;
  angle = Math.atan2( my, mx ) * 180 / Math.PI;
  log.innerHTML = '<strong>' + (dist>>0) +'</strong> pixels in <strong>'+
                  duration +'</strong> ms ( <strong>' +
                  twofloat(dist/duration) +'</strong> pixels/ms)'+
                  ' at <strong>' + twofloat(angle) +
                  '</strong> degrees';
}
function onmousemove (ev) {
  if (pressed) {
    moveplot(ev.pageX, ev.pageY);
  }
}
function twofloat(val) {
  return Math.round((val*100))/100;
}
function moveplot(x, y) {
  plot.style.left = (x - offset) + 'px';
  plot.style.top = (y - offset) + 'px';
}

OK, I admit: quite a lot is going on here. But it is not as hard as it looks. For both onmousedown and onmouseup, we read the mouse’s position with clientX and clientY and the timeStamp of the event. Mouse events have time stamps that tell you when they happened. When the mouse moves, all we check is whether the mouse button has been pressed (via a boolean set in the mousedown handler) and move the plot with the mouse.

The rest is geometry — good old Pythagoras, to be precise. We get the speed of the movement by checking the number of pixels traveled in the time difference between mousedown and mouseup.

We get the number of pixels traveled as the square root of the sum of the squares of the difference between x and y at the start and end of the movement. And we get the angle by calculating the arctangent of the triangle. All of this is covered in “A Quick Look Into the Math of Animations With JavaScript�; or you can play with the following JSFiddle example:

Media Events

Both video and audio fire a lot of events that we can tap into. The most interesting are the time events that tell you how long a song or movie has been playing. A nice little demo to look at is the MGM-inspired dinosaur animation on MDN; I recorded a six-minute screencast explaining how it is done.

If you want to see a demo of all the events in action, the JPlayer team has a great demo page showing media events.

Input Options

Traditionally, browsers gave us mouse and keyboard interaction. Nowadays, this is not enough because we use hardware that offers more to us. Device orientation, for example, allows you to respond to the tilting of a phone or tablet; touch events are a big thing on mobiles and tablets; the Gamepad API allows us to read out game controllers in browsers; postMessage allows us to send messages across domains and browser windows; pageVisibility allows us to react to users switching to another tab. We can even detect when the history object of the window has been manipulated. Check the list of events in the window object to find some more gems that might not be quite ready but should be available soon for us to dig into.

Whatever comes next in browser support, you can be sure that events will be fired and that you will be able to listen to them. The method works and actually rocks.

Go Out And Play

And that is it. Events are not hard; in most cases, you just need to subscribe to them and check what comes back as the event object to see what you can do with it. Of course, a lot of browser hacking is still needed at times, but I for one find incredible the number of ways we can interact with our users and see what they are doing. If you want to get really creative with this, stop thinking about the use cases we have now and get down into the nitty gritty of what distances, angles, speed and input can mean to an interface. If you think about it, playing Angry Birds to the largest degree means detecting the start and end of a touch event and detecting the power and direction that the bird should take off in. So, what is stopping you from creating something very interactive and cool?

Image source of picture on front page.

(al)


© Christian Heilmann for Smashing Magazine, 2012.


Teach Them How To Hit The Ground Running And Faceplant At The Same Time?


  

A few days ago, a tutorial on how to Create A Christmas Wish List With PHP was published on Smashing Magazine’s Coding section that frustrated me. It frustrated me as it was incredibly easy to predict the comment reactions it caused. It also frustrated me as it was a classic example of a tutorial resulting in very happy readers who will go out and cause a lot of terrible things on the Web unless they understand that this was meant as a “beginner tutorial”. A lot of the bad feedback was about security — something we shouldn’t take lightly.

It frustrated me mostly because it all happened on Smashing Magazine, a well-respected online publication that is read by many beginners (especially in back-end technologies) and one that is dedicated to quality content with an advisory board (one of which is me) meaning that every article gets reviewed by experts before it is published. This one slipped by in the rush of the holidays, and it was updated a couple of hours after it was published, i.e. the editors added an editor’s note and addressed some important missing points. I am happy that it was published in its original form as it inspired me to point out some things that I see happening in online magazines a lot lately.

The predictable outcome of this kind of tutorial is:

  • Seasoned developers will find issues with the code and claim that it should not be done that way.
  • Other people will disagree and tell the old men to stop telling young kids to get off their lawn.
  • Real beginners will chime in and say that they are very happy about the article and getting the feeling that things are not as complex as they seem to be.
  • A lot of fanboys will mention technology XYZ that makes this much easier.
  • The author will add more disclaimers about the nature of the code within the article with some edits and add warning messages about its viability in the wild — saying that this is just demo code.

Quick Wins Full Of Traps

“Quick tutorials for beginners” are killing our craft. Instead of pointing to existing documentation and keeping it up to date (in the case of the wiki-based docs out there) every new developer turned to an author wanting the fame for themselves. And a lot of online magazines cater to these to achieve “new” content and thus visitors. We measure our success by the number of hits, the traffic, the comments and retweets. And to get all of that, we want to become known as someone who wrote that “very simple article that allowed me to do that complex thing in a matter of minutes”.

Teacher/Learner

Image credit: Opensourceway.

Instead of teaching the underlying technology, we tend to show a quick, beautiful implementation and put a lot of effort into it. We teach a “create something amazing in 5 minutes” and hope people will care enough afterwards and look at learning the underlying technologies. We aim to whet their appetite whilst giving them full solutions. The reason is that this is exactly what we wished we had had when we learned that thing in the first place. Sadly, this is not how teaching and learning works.

Road Safety Begins In A Classroom

At this moment, let me go back in time a bit. Growing up in a small village having a driving license and subsequently a car was a vital part of your social life and also your work options. Therefore, I couldn’t wait to get mine.

Now, what you want to do is to learn driving. You want to get into the car, go vroom-vroom and be off. The reality of getting a driving license though (at least in Germany where there are no speed limits on the motorway and therefore it is taken very seriously) is that you spend quite a lot of evenings in a boring classroom before you get behind the wheel. You learn about the code of the street, the different signs and what to do in all kind of situations in a car. You even learn about the different parts of the car and what they do.

The reason is that it scales better — you need to learn all that stuff and it is much easier to pack 40 students in a room to teach the basics before you try to make up a schedule where all of them can drive out on the road. As a driving school, instead of 40 cars you can get by with 5. And students who already know what they should not do and where things are in a car are less likely to crash them.

Educators Learning From Bad Experiences?

This is frustrating and annoying, the same way learning things at school without being told what they are good for is surely annoying. On the Web, we want to be different. We want to make learning fun and we are tempted to put in as much as possible for beginners so they can get past the basics very quickly and build the awesome of tomorrow instead. The author actually mentions that in the comments:

“I think teaching people to do things is very complicated, doubly so over the internet. If I were teaching a university class I would take a very different approach.”

Yes, teaching is hard. That’s why not every gifted developer is also good at explaining or a good trainer.

While it is a very good idea in our heads to give people quick solutions with real results instead of step-by-step basics, we forget how we actually got there. Once we reached the level in a skill to be educators in it, we went through a lot of trial and error using the skill. By avoiding this, we strip others of the chance to learn a skill on their own terms and with their own obstacles to overcome.

How About Writing Beginner Tutorials Covering Beginner Tasks?

So, I think it is safe to assume that there are two needs/aims battling when we want to write a beginner tutorial, i.e. we want to teach people good practices and we want to get them as far as possible with the least effort. A lot of times these don’t go well together.

jQuery is a poster child of great “new” Web development. “Write less, achieve more” is the mantra and I love that we have it. jQuery achieved this by replacing JavaScript and the unwieldy DOM with a clever and fast API and a totally new syntax: chaining. This is great. This is how to do it. jQuery abstracts the annoyances and complexities out into its core and lets developers write code. You cannot just take this approach and mantra and apply it to any technology without providing a simpler API/platform that abstracts the dangers and annoyances.

Teaching Non-Live Code On The Web?

The discussion that happened in the comments of the aforementioned article was mostly about security and the inability of implementing the code discussed in it in a real environment. And yes, they are very much valid. The code is good as an exercise but awful as a live example. Putting it on an live server means you are open to any kind of attacks and scripts looking for zombies to infect — not to mention how a botnet would have a field day with it!

And the author knows this. This is why a lot of the article is dedicated to explaining that this is not live code:

“Please notice that this article was written for beginners who already grasp HTML and CSS, know a bit of PHP and have seen phpMyAdmin before. I will not go into best practices, safety and all the rest of it; let’s just have fun with this one!”

And later on — as a response to some feedback, even more “don’t do this” was added:

“Note that this is meant as a beginner’s exercise. The code you see here will give you the intended result, but a lot of it is not safe for production websites. It lacks a lot of safeguards, such as data validation, salts for passwords (for better security), htaccess rules and so on. The goal of this article is to let beginners forget about all of these things and just concentrate on building something nice.

Neither does this article promote best practices. You may find yourself adopting different methods later on, or I may write in another article that we shouldn’t do something you see here. The article is intended as a fun little example for beginners to spice up their boring theory sessions. I believe that the best way to learn is through increasingly difficult examples.

That said, I encourage you to try all of this out and play around with it at home or on your servers. If you put this on a live server, I recommend using an account that has only this website on it (or only test websites). I also recommend using passwords for user accounts that are not the same as your other passwords.”

This, actually very much is against the very idea of a beginner tutorial. A beginner tutorial gets people on the way, i.e. it teaches them the first steps and what one can do with it. As these quotes show, teaching people PHP by starting with SQL and writing a login system and file uploader is obviously the wrong way.

Out of a sudden, the simple beginner tutorial is “intended as a fun little example for beginners to spice up their boring theory sessions” (cited). What boring theory sessions? I thought we are building something from scratch here?

Piling On Too Much

The article tries to teach four things at once: SQL with PHP, login and session control, file uploads and how to build a beautiful Web interface powered by PHP. The login system and the file upload is where it gets very dangerous in terms of security. This is not a beginner tutorial — it is giving beginners the wrong impression that everything is easy and everybody else probably just does it wrong and cares far too much about boring details.

We should not teach new developers that they can do things in a few lines of code and keep quiet about the bad effects this has. This is condescending and based on an assumption that people learn only from successes on the Web. The author mentions that in the comments:

“I don’t think beginners need to concern themselves with SQL injection attacks. The point here is to start to learn something, not to learn everything at once. When someone understands SQL at all, then teach them about the problems, not before.”

This is very dangerous thinking — if you teach how to do something, also make people aware of the consequences it has. I totally agree that the point is to learn something. Defining the “something” is the skill of a good tutorial writer or educator. We focus far too much on the final product to be built, rather than the components we use to get there.

This is where using a complex example like a “Christmas Wishlist” that needs a login, uses a database and has an upload feature for any file is a bad choice. There is no way to keep this “simple” unless you teach people how to write code exclusively for their own localhost.

Let’s Not Assume That People Read And Care As Much As We Think They Do

One comment was quite interesting as a summary, as it very much sums up some of the comments and assumes good on the side of the readers:

“Good stuff just to have some fun and help the super beginners get a quick footing. I think a lot of the people commenting here are either A) Too seasoned to look this far back, and not doing things the “properâ€� way just irks them, or B) I’d be willing to bet some are just flexing their programmer’s ego a bit.

I think assuming that people will take this as serious programming and build from it, building the wrong way, is a bit too much of a stretch. Anyone who can read and who cares about doing things the right way will take the author’s disclaimer to heart. If not, odds are they’re looking for the easy route. If that’s the case, you can’t really stop them. This article isn’t ending the world.”

I agree, it is not. But it also brings nothing new to the table. When I learned PHP coming from Perl in around 2000, I read thickbook.com and — except for the CSS styles — it had similar examples. Over the years we learned to protect our systems more. I think the assumption that readers will care much about the “this is not live code” doesn’t cover one main use case of “beginner tutorials”, i.e. that people will most probably find the article via a Google search and simply use the code example in a live environment without reading the tutorial or the comments. All they wanted was a quick, simple to understand example after all and beginner tutorials have those, right?

In My Humble Opinion

Image credit: Opensourceway.

Want proof of that? Look at the success of W3Schools.com. The Web is full of materials to learn the same things. The quick “here’s the solution — don’t worry about how it works right now” are the most successful ones. We also have a Web full of systems that lack very basic quality and security features and we spend months educating hires in companies what developing production code means when you protect the data of our users.

I think it is time to stop chasing the hollow success of creating a “quick tutorial” that is actually a “bad implementation with quick, sloppy code” in disguise and start curating what is already on the Web. We can then concentrate on the next level tutorials.

I think Web-based education will be a big thing in the near future, and creating a new generation of Web makers should be on all of our agendas. We do this with tools, great documentation and frameworks, and not with a “write this, it is awesome” approach.

(il)


© Christian Heilmann for Smashing Magazine, 2011.


A Quick Look Into The Math Of Animations With JavaScript





 



 


In school, I hated math. It was a dire, dry and boring thing with stuffy old books and very theoretical problems. Even worse, a lot of the tasks were repetitive, with a simple logical change in every iteration (dividing numbers by hand, differentials, etc.). It was exactly the reason why we invented computers. Suffice it to say, a lot of my math homework was actually done by my trusty Commodore 64 and some lines of Basic, with me just copying the results later on.

These tools and the few geometry lessons I had gave me the time and inspiration to make math interesting for myself. I did this first and foremost by creating visual effects that followed mathematical rules in demos, intros and other seemingly pointless things.

There is a lot of math in the visual things we do, even if we don’t realize it. If you want to make something look natural and move naturally, you need to add a bit of physics and rounding to it. Nature doesn’t work in right angles or linear acceleration. This is why zombies in movies are so creepy. This was covered here before in relation to CSS animation, but today let’s go a bit deeper and look at the simple math behind the smooth looks.

Going From 0 To 1 Without Being Boring

If you’ve just started programming and are asked to go from 0 to 1 with a few steps in between, you would probably go for a for loop:

for ( i = 0; i <= 1; i += 0.1 ) {
  x = i;
  y = i;
…
}

This would result in a line on a graph that is 45 degrees. Nothing in nature moves with this precision:

45 degree graph, a result of a simple for loop

A simple way to make this movement a bit more natural would be to simply multiply the value by itself:

for ( i = 0; i <= 1; i += 0.1 ) {
  x = i;
  y = i * i;
}

This means that 0.1 is 0.01, 0.2 is 0.04, 0.3 is 0.09, 0.4 is 0.16, 0.5 is 0.25 and so on. The result is a curve that starts flat and then gets steeper towards the end:

graph of the value multiplied with itself

You can make this even more pronounced by continuing to multiply or by using the “to the power of� Math.pow() function:

for ( i = 0; i <= 1; i += 0.1 ) {
  x = i;
  y = Math.pow( i, 4 );
}

graph with several multiplications

This is one of the tricks of the easing functions used in libraries such as jQuery and YUI, as well as in CSS transitions and animations in modern browsers.

You can use this the same way, but there is an even simpler option for getting a value between 0 and 1 that follows a natural motion.

Not A Sin, Just A Natural Motion

Sine waves are probably the best thing ever for smooth animation. They happen in nature: witness a spring with a weight on it, ocean waves, sound and light.
In our case, we want to move from 0 to 1 smoothly.

To create a movement that goes from 0 to 1 and back to 0 smoothly, we can use a sine wave that goes from 0 to π in a few steps. The full sine wave going from 0 to π × 2 (i.e. a whole circle) would result in values from -1 to 1, and we don’t want that (yet).

var counter = 0;

// 100 iterations
var increase = Math.PI / 100;

for ( i = 0; i <= 1; i += 0.01 ) {
  x = i;
  y = Math.sin(counter);
  counter += increase;
}

half a sine wave

A quick aside on numbers for sine and cosine: Both Math.sin() and Math.cos() take as the parameter an angle that should be in radians. As humans, however, degrees ranging from 0 to 360 are much easier to read. That’s why you can and should convert between them with this simple formula:

var toRadian = Math.PI / 180;
var toDegree = 180 / Math.PI;

var angle = 30;

var angleInRadians = angle * toRadian;
var angleInDegrees = angleInRadians * toDegree;

Back to our sine waves. You can play with this a lot. For example, you could use the absolute value of a full 2 × π loop:

var counter = 0;
// 100 iterations
var increase = Math.PI * 2 / 100;

for ( i = 0; i <= 1; i += 0.01 ) {
  x = i;
  y = Math.abs( Math.sin( counter ) );
  counter += increase;
}

absolute value of a sine wave

But again, this looks dirty. If you want the full up and down, without a break in the middle, then you need to shift the values. You have to half the sine and then add 0.5 to it:

var counter = 0;
// 100 iterations
var increase = Math.PI * 2 / 100;

for ( i = 0; i <= 1; i += 0.01 ) {
  x = i;
  y = Math.sin( counter ) / 2 + 0.5;
  counter += increase;
}

halfed and moved sine wave

So, how can you use this? Having a function that returns -1 to 1 to whatever you feed it can be very cool. All you need to do is multiply it by the values that you want and add an offset to avoid negative numbers.

For example, check out this sine movement demo:

Sine jump demo

Looks neat, doesn’t it? A lot of the trickery is already in the CSS:

.stage {
  width:200px;
  height:200px;
  margin:2em;
  position:relative;
  background:#6cf;
  overflow:hidden;
}

.stage div {
  line-height:40px;
  width:100%;
  text-align:center;
  background:#369;
  color:#fff;
  font-weight:bold;
  position:absolute;
}

The stage element has a fixed dimension and is positioned relative. This means that everything that is positioned absolutely inside it will be relative to the element itself.

The div inside the stage is 40 pixels high and positioned absolutely. Now, all we need to do is move the div with JavaScript in a sine wave:

var banner = document.querySelector( '.stage div' ),
    start = 0;
function sine(){
  banner.style.top = 50 * Math.sin( start ) + 80 + 'px';
  start += 0.05;
}
window.setInterval( sine, 1000/30 );

The start value changes constantly, and with Math.sin() we get a nice wave movement. We multiply this by 50 to get a wider wave, and we add 80 pixels to center it in the stage element. Yes, the element is 200 pixels high and 100 is half of that, but because the banner is 40 pixels high, we need to subtract half of that to center it.

Right now, this is a simple up-and-down movement. Nothing stops you, though, from making it more interesting. The multiplying factor of 50, for example, could be a sine wave itself with a different value:

var banner = document.querySelector( '.stage div' ),
    start = 0,
    multiplier = 0;
function sine(){
  multiplier = 50 * Math.sin( start * 2 );
  banner.style.top = multiplier * Math.sin( start ) + 80 + 'px';
  start += 0.05;
}
window.setInterval( sine, 1000/30 );

The result of this is a banner that seems to tentatively move up and down. Back in the day and on the very slow Commodore 64, calculating the sine wave live was too slow. Instead, we had tools to generate sine tables (arrays, if you will), and we plotted those directly. One of the tools for creating great sine waves so that you could have bouncing scroll texts was the Wix Bouncer:

Wixbouncer - a tool to create sine waves

Circles In The Sand, Round And Round…

Circular motion is a thing of beauty. It pleases the eye, reminds us of spinning wheels and the earth we stand on, and in general has a “this is not computer stuff� feel to it. The math of plotting something on a circle is not hard.

It goes back to Pythagoras, who, as rumor has it, drew a lot of circles in the sand until he found his famous theorem. If you want to use all the good stuff that comes from this theorem, then try to find a triangle with a right angle. If this triangle’s hypothenuse is 1, then you can easily calculate the horizontal leg as the cosine of the angle and the vertical leg as the sine of the angle:

Sincos

How is this relevant to a circle? Well, it is pretty simple to find a right-angle triangle in a circle to every point of it:

Circle

This means that if you want to plot something on a circle (or draw one), you can do it with a loop and sine and cosine. A full circle is 360°, or 2 × π in radians. We could have a go at it — but first, some plotting code needs to be done.

A Quick DOM Plotting Routine

Normally, my weapon of choice here would be canvas, but in order to play nice with older browsers, let’s do it in plain DOM. The following helper function adds div elements to a stage element and allows us to position them, change their dimensions, set their color, change their content and rotate them without having to go through the annoying style settings on DOM elements.

Plot = function ( stage ) {

  this.setDimensions = function( x, y ) {
    this.elm.style.width = x + 'px';
    this.elm.style.height = y + 'px';
    this.width = x;
    this.height = y;
  }
  this.position = function( x, y ) {
    var xoffset = arguments[2] ? 0 : this.width / 2;
    var yoffset = arguments[2] ? 0 : this.height / 2;
    this.elm.style.left = (x - xoffset) + 'px';
    this.elm.style.top = (y - yoffset) + 'px';
    this.x = x;
    this.y = y;
  };
  this.setbackground = function( col ) {
    this.elm.style.background = col;
  }
  this.kill = function() {
    stage.removeChild( this.elm );
  }
  this.rotate = function( str ) {
    this.elm.style.webkitTransform = this.elm.style.MozTransform =
    this.elm.style.OTransform = this.elm.style.transform =
    'rotate('+str+')';
  }
  this.content = function( content ) {
    this.elm.innerHTML = content;
  }
  this.round = function( round ) {
    this.elm.style.borderRadius = round ? '50%/50%' : '';
  }
  this.elm = document.createElement( 'div' );
  this.elm.style.position = 'absolute';
  stage.appendChild( this.elm );

};

The only things that might be new here are the transformation with different browser prefixes and the positioning. People often make the mistake of creating a div with the dimensions w and h and then set it to x and y on the screen. This means you will always have to deal with the offset of the height and width. By subtracting half the width and height before positioning the div, you really set it where you want it — regardless of the dimensions. Here’s a proof:

offset issue

Now, let’s use that to plot 10 rectangles in a circle, shall we?

Simple circle

var stage = document.querySelector('.stage'),
    plots = 10;
    increase = Math.PI * 2 / plots,
    angle = 0,
    x = 0,
    y = 0;

for( var i = 0; i < plots; i++ ) {
  var p = new Plot( stage );
  p.setBackground( 'green' );
  p.setDimensions( 40, 40 );
  x = 100 * Math.cos( angle ) + 200;
  y = 100 * Math.sin( angle ) + 200;
  p.position( x, y );
  angle += increase;
}

We want 10 things in a circle, so we need to find the angle that we want to put them at. A full circle is two times Math.PI, so all we need to do is divide this. The x and y position of our rectangles can be calculated by the angle we want them at. The x is the cosine, and the y is the sine, as explained earlier in the bit on Pythagoras. All we need to do, then, is center the circle that we’re painting in the stage (200,200 is the center of the stage), and we are done. We’ve painted a circle with a radius of 100 pixels on the canvas in 10 steps.

The problem is that this looks terrible. If we really want to plot things on a circle, then their angles should also point to the center, right? For this, we need to calculate the tangent of the right-angle square, as explained in this charming “Math is fun� page. In JavaScript, we can use Math.atan2() as a shortcut. The result looks much better:

Tancircle

var stage = document.querySelector('.stage'),
    plots = 10;
    increase = Math.PI * 2 / plots,
    angle = 0,
    x = 0,
    y = 0;

for( var i = 0; i < plots; i++ ) {
  var p = new Plot( stage );
  p.setBackground( 'green' );
  p.setDimensions( 40, 40 );
  x = 100 * Math.cos( angle ) + 200;
  y = 100 * Math.sin( angle ) + 200;
  p.rotate( Math.atan2( y - 200, x - 200 ) + 'rad' );
  p.position( x, y );
  angle += increase;
}

Notice that the rotate transformation in CSS helps us heaps in this case. Otherwise, the math to rotate our rectangles would be much less fun. CSS transformations also take radians and degrees as their value. In this case, we use rad; if you want to rotate with degrees, simply use deg as the value.

How about animating the circle now? Well, the first thing to do is change the script a bit, because we don’t want to have to keep creating new plots. Other than that, all we need to do to rotate the circle is to keep increasing the start angle:

var stage = document.querySelector('.stage'),
    plots = 10;
    increase = Math.PI * 2 / plots,
    angle = 0,
    x = 0,
    y = 0,
    plotcache = [];

for( var i = 0; i < plots; i++ ) {
  var p = new Plot( stage );
  p.setBackground( 'green' );
  p.setDimensions( 40, 40 );
  plotcache.push( p );
}

function rotate(){
  for( var i = 0; i < plots; i++ ) {
    x = 100 * Math.cos( angle ) + 200;
    y = 100 * Math.sin( angle ) + 200;
    plotcache[ i ].rotate( Math.atan2( y - 200, x - 200 ) + 'rad' );
    plotcache[ i ].position( x, y );
    angle += increase;
  }
  angle += 0.06;
}

setInterval( rotate, 1000/30 );

Want more? How about a rotating text message based on this? The tricky thing about this is that we also need to turn the characters 90° on each iteration:

Rotated message

var stage = document.querySelector('.stage'),
    message = 'Smashing Magazine '.toUpperCase(),
    plots = message.length;
    increase = Math.PI * 2 / plots,
    angle = -Math.PI,
    turnangle = 0,
    x = 0,
    y = 0,
    plotcache = [];

for( var i = 0; i < plots; i++ ) {
  var p = new Plot( stage );
  p.content( message.substr(i,1) );
  p.setDimensions( 40, 40 );
  plotcache.push( p );
}
function rotate(){
  for( var i = 0; i < plots; i++ ) {
    x = 100 * Math.cos( angle ) + 200;
    y = 100 * Math.sin( angle ) + 200;
    // rotation and rotating the text 90 degrees
    turnangle = Math.atan2( y - 200, x - 200 ) * 180 / Math.PI + 90 + 'deg';
    plotcache[ i ].rotate( turnangle );
    plotcache[ i ].position( x, y );
    angle += increase;
  }
  angle += 0.06;
}

setInterval( rotate, 1000/40 );

Again, nothing here is fixed. You can make the radius of the circle change constantly, as we did with the bouncing banner message earlier (below is only an excerpt):

multiplier = 80 * Math.sin( angle );
for( var i = 0; i < plots; i++ ) {
  x = multiplier * Math.cos( angle ) + 200;
  y = multiplier * Math.sin( angle ) + 200;
  turnangle = Math.atan2( y - 200, x - 200 ) * 180 / Math.PI + 90 + 'deg';
  plotcache[ i ].rotate( turnangle );
  plotcache[ i ].position( x, y );
  angle += increase;
}
angle += 0.06;

And, of course, you can move the center of the circle, too:

rx = 50 * Math.cos( angle ) + 200;
ry = 50 * Math.sin( angle ) + 200;
for( var i = 0; i < plots; i++ ) {
  x = 100 * Math.cos( angle ) + rx;
  y = 100 * Math.sin( angle ) + ry;
  turnangle = Math.atan2( y - ry, x - rx ) * 180 / Math.PI + 90 + 'deg';
  plotcache[ i ].rotate( turnangle );
  plotcache[ i ].position( x, y );
  angle += increase;
}
angle += 0.06;

For a final tip, how about allowing only a certain range of coordinates?

function rotate() {
  rx = 70 * Math.cos( angle ) + 200;
  ry = 70 * Math.sin( angle ) + 200;
  for( var i = 0; i < plots; i++ ) {
    x = 100 * Math.cos( angle ) + rx;
    y = 100 * Math.sin( angle ) + ry;
    x = contain( 70, 320, x );
    y = contain( 70, 320, y );
    turnangle = Math.atan2( y - ry, x - rx ) * 180 / Math.PI + 90 + 'deg';
    plotcache[ i ].rotate( turnangle );
    plotcache[ i ].position( x, y );
    angle += increase;
  }
  angle += 0.06;
}
function contain( min, max, value ) {
  return Math.min( max, Math.max( min, value ) );
}

Summary

This was just a quick introduction to using exponentials and sine waves and to plotting things on a circle. Have a go with the code, and play with the numbers. It is amazing how many cool effects you can create with a few changes to the radius or by multiplying the angles. To make it easier for you to do this, below are the examples on JSFiddle to play with:

(il) (al)


© Christian Heilmann for Smashing Magazine, 2011.


Syncing Content With HTML5 Video

Advertisement in Syncing Content With HTML5 Video
 in Syncing Content With HTML5 Video  in Syncing Content With HTML5 Video  in Syncing Content With HTML5 Video

One of the main changes from HTML4 to HTML5 is that the new specification breaks a few of the boundaries that browsers have been confined to. Instead of restricting user interaction to text, links, images and forms, HTML5 promotes multimedia, from a generic <object> element to a highly specified <video> and <audio> element, and with a rich API to access in pure JavaScript.

Native multimedia capability has a few benefits:

  • End users have full control over the multimedia.
    The native controls of browsers allow users to save videos locally or email them to friends. Also, HTML5 video and audio are keyboard-enabled by default, which is a great accessibility benefit.
  • End users do not need to install a plug-in to play them.
    The browser already has everything it needs to play movies and sound.
  • Video and audio content on the page can be manipulated.
    They are simply two new elements like any other that can be styled, moved, manipulated, stacked and rotated.
  • You can build your own controls using HTML, CSS and JavaScript.
    No new skills or development environment needed.
  • Interaction with the rest of the page is simple.
    The multimedia API gives you full control over the video, and you can make the video react both to changes in the video itself and to the page around it.

Let’s quickly recap how you can use native video in the browser, starting with the embedding task.

Embedding Video

This is old news. Embedding video in a document is as easy as adding a <video> element and pointing it to the source video. Adding a controls attribute gives you native controls:

<video src="chris.ogv" controls></video>

This is the theory, though. In the real world of intellectual property, corporate competition and device-specific solutions, we as developers have to jump through a few hoops:

<video controls="true" height="295" width="480">
  <!-- hello iOS, Safari and IE9 -->
  <source src="chris.mp4" type="video/mp4">
  <!-- Hello Chrome and Firefox (and Opera?) -->
  <source src="chris.webm" type="video/webm">
  <!-- Hello Firefox and Opera -->
  <source src="chris.ogv" type="video/ogg">
  <!-- Hello legacy -->
  Your browser does not support the video tag,
  <a href="http://www.youtube.com/watch?v=IhkUe_KryGY">
    check the video on YouTube
  </a>.
</video>

This shows how we need to deliver video in three formats in order to satisfy all of the different browsers out there. There are a few ways to accomplish this. Here’s what I do…

Convert Video With Miro Video Converter

Miro Video Converter is an open-source tool for Mac that makes converting videos dead easy. Simply drag the video to the tool, select WebM as the output format, and watch the progress. A few other converters for Windows and Linux are available, too.

Miro in Syncing Content With HTML5 Video

Hosting And Automated Conversion On Archive.org

Because I license my videos with Creative Commons, I can use Archive.org to both host the videos and convert the WebM versions to MP4 and OGV. Simply upload your video and wait about an hour. Reload the page, and the server pixies at Archive.org will have created the other two formats (and also a cool animated GIF of your video).

Archive-org in Syncing Content With HTML5 Video
You can use Archive.org to both host the videos and convert the WebM versions to MP4 and OGV. Large view.

Industrial-Strength Conversion With Vid.ly

WebM, OGV and MP4 take care of only the major browsers, though. If you want to support all mobile devices, tablets and consoles and you want the video quality to adapt to the user’s connection speed, then you’ll have to create a few dozen versions of the same video. Encoding.com feels our pain and has released a free service called Vid.ly, which converts any video you upload into many different formats more or less in real time. Unfortunately, the service is in private beta at the moment, but you can use the invite code HNY2011.

Vid-ly in Syncing Content With HTML5 Video
Vid.ly converts any video you upload into many different formats more or less in real time. Large view.

Furthermore, Vid.ly creates a URL for your video that automatically redirects the browser or device calling it to the right format. This keeps your embed code as simple as possible:

<video src="http://vid.ly/4f3q1f?content=video" controls></video>

Cool, isn’t it?

The Power Of The HTML5 Video API: Syncing Content

Now that our video is on the page, let’s check out the power of the API. Say, for example, you want to know what part of the movie is playing right now. This is as simple as subscribing to an event of the <video> element:

<div id="stage">
  <video src="http://vid.ly/4f3q1f?content=video" controls></video>
  <div id="time"></div>
</div>
<script>
  (function(){
    var v = document.getElementsByTagName('video')[0]
    var t = document.getElementById('time');
    v.addEventListener('timeupdate',function(event){
      t.innerHTML = v.currentTime;
    },false);
  })();
</script>

If you try this out in your browser, you will see the current time below the video when you play it.

Time in Syncing Content With HTML5 Video

You will also see that the timeupdate event gets fired a lot and at somewhat random times. If you want to use this to sync the showing and hiding of parts of the document, then you’ll need to throttle it somehow. The easiest way to do this is to limit the number to full seconds using parseInt():

<div id="stage">
  <video src="http://vid.ly/4f3q1f?content=video" controls></video>
  <div id="time"></div>
</div>
<script>
  (function(){
    var v = document.getElementsByTagName('video')[0]
    var t = document.getElementById('time');
    v.addEventListener('timeupdate',function(event){
      t.innerHTML = parseInt(v.currentTime) + ' - ' + v.currentTime;
    },false);
  })();
</script>

Timesecond in Syncing Content With HTML5 Video

You can use this to trigger functionality at certain times. For example, you can sync an Indiana Jones-style animation of a map to a video:

For a full explanation of this demo, check out the blog post on Mozilla Hacks.

Let’s have a go at something similar: a video that shows the content from web pages being referred to by a presenter. Check out this video demo of me explaining what we’re doing here, with the content appearing and disappearing at certain times in the video. Make sure to jump around the video with the controls.

Syncing in Syncing Content With HTML5 Video

We’ve already covered how to get the current time of a video in seconds. What I want now is to display and hide a few parts of the website at certain times in the video. If video is not supported in the browser, then I would just show all of the content without any syncing.

The first issue I have to solve is to allow the maintainer to control what is shown when. Normally, I’d use a JSON object in the JavaScript, but I figure that keeping the maintenance in the markup itself makes much more sense.

HTML5 allows you to store information in data- attributes. So, to make it easy to tell the script when to show what, I just use data-start and data-end attributes, which define the time frames for the articles that I want to sync with the video:

<article data-start="64" data-end="108">
  <header><h1>Archive.org for conversion</h1></header>
  <p><a href="http://archive.org">Archive.org</a> is a website dedicated to
archiving the Internet. For content released as under a Creative Commons
license, it offers hosting for video and audio and automatically converts the
content to MP4 and Ogg video for you.</p>
  <iframe src="http://archive.org"></iframe>
</article>

You can try it out by downloading the code and changing the values yourself (or use Firebug or the Web Inspector to change it on the fly).

Here’s the script (using jQuery) that makes this happen:

/* if the document is ready… */
$(document).ready(function(){

/* if HTML5 video is supported */
  if($('video').attr('canPlayType')){

    $('aside::first').append('<p>Play the video above and see how ' +
                             'the different connected content sections ' +
                             'in the page appear at the right moment. '+
                             'Feel free to jump forward and backward</p>');

    var timestamps = [],
        last = 0,
        all = 0,
        now = 0,
        old = 0,
        i=0;

/* hide all articles via CSS */
    $('html').addClass('js');

/*
   Loop over the articles, read the timestamp start and end and store
   them in an array
*/
    $('article').each(function(o){
      if($(this).attr('data-start')){
        timestamps.push({
          start : +$(this).attr('data-start'),
          end : +$(this).attr('data-end'),
          elm : $(this)
        });
      }
    });

    all = timestamps.length;

/*
  when the video is playing, round up the time to seconds and call the
  showsection function continuously
*/
    $('video').bind('timeupdate',function(event){
      now = parseInt(this.currentTime);

      /* throttle function calls to full seconds */
      if(now > old){
        showsection(now);
      }
      old = now;

    });

/*
  Test whether the current time is within the range of any of the
  defined timestamps and show the appropriate section.
  Hide all others.
*/
    function showsection(t){
      for(i=0;i<all;i++){
        if(t >= timestamps[i].start && t <= timestamps[i].end){
          timestamps[i].elm.addClass('current');
        } else {
          timestamps[i].elm.removeClass('current');
        }
      }
    };

  };
});

So, what’s going on here? First, we’re checking whether the browser is capable of playing HTML5 video by testing for the canPlayType attribute. If all is fine, then we add some explanatory text to the document (which wouldn’t make sense if the browser couldn’t show a video). Then, we define some variables to use and add a class of js to the root element of the document. This, together with the .js article selector in the CSS, hides all of the articles in the document.

We then loop through the articles, read out the timestamps for the start and end of each of the sections and store them in an array called timestamps.

We then subscribe to the timeupdate event, rounded up to full seconds, and call the showsection() function every new second.

The showsection() function loops through all of the timestamps and tests whether the current time of the video is in the range of one of the articles. If it is, then that article is displayed (by adding a current class) and all the others are hidden. This could be optimized by storing the current section and removing the class from only that element.

Can We Do The Same With Less Or No Code?

If you like the idea of syncing content and video, check out the Popcorn framework, which is based on the same techniques but gives you much more control over the video itself.

Popcorn in Syncing Content With HTML5 Video

Butter is a point-and-click interface to go on top of Popcorn. It has a nice timeline editor that allows you to play a video and show all kinds of Web content at certain times. You can export and send your creations to friends, too.

Butter in Syncing Content With HTML5 Video
Large view

With systems like Popcorn and Butter, we are one step closer to having authoring tools for the rich interactions that HTML5 offers us. What can you think of building?

Summary

Today we looked at how to embed video onto a Web document; and with the native video API that gives us event handlers for changes in a video, we saw how easy it is to make the video interact with the rest of the document. Instead of trying to control the video, we use native controls to make the page react to what is happening in the video itself. We used semantic HTML and data attributes to allow maintainers to use the syncing script without having to touch any JavaScript, and we looked at some services that make hosting and converting video easy.

All of these cool technologies give us a lot of power, but we can’t just, say, write some simple CSS, JavaScript and HTML to use them. If we want open technologies to succeed, then we have to make them easy for people to use. The next step now is to move from the “one-off implementation� phase and think about creating tools and step-by-step code-creation systems for users who want to use these cool new technologies but don’t want to spend much time and effort doing it.

With native audio and video in browsers, we’ve taken a massive step toward make the open Web more engaging and beautiful. The next step will be to use multimedia not only for output but for input. A lot of hardware these days comes with cameras and microphones; we need to start using and supporting open technology that allows our users to take advantage of this hardware to interact with our Web products.

For a screencast on this topic, see Syncing page content with HTML5 video on the Mozilla Hacks blog.

(al)



© Christian Heilmann for Smashing Magazine, 2011. | Permalink | Post a comment | Smashing Shop | Smashing Network | About Us
Post tags: ,


  •   
  • Copyright © 1996-2010 BlogmyQuery - BMQ. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress