hungryturtlecode

Hungry Turtle Code Logo

Fun with jQuery Keyboard Events

Please share this post if you enjoy it!

How do I track keystrokes with jQuery?

I often hear people who are just getting into programming ask things like, “but how do I use this in the real world?”. It is all well and good understanding things from a theoretical standpoint, but it’s all about actually using that knowledge to make something awesome.

So today I am going to explain how you can build a pretty simple effect using jQuery by monitoring user keystrokes.

But What Is This Awesome Effect?

It is a recreation of a marketing campaign that Honda used a few years ago. The campaign itself was called “The Other Side” and was used to showcase the difference in driver experience between two of their cars.

The effect itself is best experienced than explained, but unfortunately the original webpage doesn’t exist anymore. You can watch the video tutorial below and I demo the effect.

To try (and probably fail) to explain the effect – a video is played which looks like a typical advert for this car, but then it prompts you to press the “r” key. When you press the “r” key, the video switches to a different advert for a different car.

However, the clever bit is that each of the two adverts are practically identical, one just has a “light” happy feeling while the other has a darker more adventurous feel.

By pressing and releasing the “r” key, you can switch back and forth between these two “parallel universes” so to speak.

You Talk Too Much; Show Me Some Code!

Head over to the Github repo and you will be able to get the video assets you need. You can also take a look at the code from the tutorial.

Reading is cool though…

The Other Side – A Story of Two Parts.

So I decided to split this tutorial into two separate parts. The first part (this part) will purely be focusing on just creating the effect as quickly as possible. Let’s just get some results!

Part 2 can be found here.

The code written in this tutorial will be very much in the style of a typical beginner JavaScript and specifically jQuery developer.

If just creating the effect is all you are after, this tutorial is probably all you need. If you are wanting to improve as a JS developer, then part 2 is right up your alley.

In the next part we will take the code we have written in the part and refactor it into a much more modular style and get the code working a lot more efficiently with better general coding practices.

Let’s Get Cracking Shall We

If you haven’t seen this effect already, watch the first minute of the above video where I briefly demo the original advert. It will really help your understanding of what’s going on if you have seen it.

So to start off, this is the HTML structure we will need to get going.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html/>
<html lang="en"/>
<head>
  <meta charset="UTF-8"/>
  <title>Honda Test</title>
  <link rel="stylesheet" href="css/style.css"/>
</head>
<body>
  <h1>How did Honda do the "Double Sided Story"?</h1>
  <div class="container">
    <div id="darkvidcont" class="vidcont">
      <video 
        id="darkvid" 
        muted src="assets\dark side final.mp4" 
        controls 
        width="1260" 
        height="536">
      </video>
    </div>
    <div id="lightvidcont" class="vidcont"/>
      <video 
        id="lightvid" 
        src="assets\light side final.mp4" 
        controls 
        width="1260" 
        height="536">
      </video>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

There isn’t anything fancy going on here. The key parts are the two videos and their containing divs. Of course, we only want to see and hear one video at any given time, so we mute the video inside id="darkvidcont" initially.

Before we get started with the JavaScript there are a few little tricky things we have to take care of with the CSS.

The way the effect will work is both videos will play at the same time, they will just be on top of each other. Only the volume of the visible video will be on – this is why we muted the other video in the HTML. This gives the appearance that there is only one video on the page.

When the user presses the “r” key (or whatever key you want to use) the currently visible video will be muted, then moved off the screen. This will expose the video behind it, which will be unmuted – leaving the impression that that is now the only video playing.

When the key is released, the reverse occurs and the originally visible video is now back in place and unmuted.

What Has This Got To Do With CSS?

Well, the easiest way to move elements back and forth off the screen is position: absolute;and then programmatically change the position rules (top, bottom, left right).

Easy! Right? We can simply reference the “vidcont” class, which references the respective containers for the two videos.

.vidcont{
  position: absolute;
}

Unfortunately, as some of you may know, when you position an element absolutely the default frame of reference to position it to becomes the entire window. So if we were to set top: 0; left: 0; we would put the videos in the very top left of the window.

This isn’t what we want. Instead, we want it to be positioned relative to our overall container (which has the convenient class of “container”).

The Trick With position: absolute

The way position absolute works is it needs a frame of reference. That frame of reference is “the nearest parent element that is positioned”.

What is meant by “element that is positioned” is an element that is either position: relative; or position: absolute; . Any normal element you create is not positioned as the default for HTML elements is position: static;.

A simple way around this is to just give our <div class="container">  CSS rule of position relative. This won’t affect where on the page the element is unless we change the top, left etc rules.

But it will now become the frame of reference for any absolutely positioned elements inside it.

So now we can add that as well as some house keeping CSS:

1
2
3
4
5
6
7
8
9
10
.container{
  margin: 50px auto;
  width: 1260px;
  position: relative;
}

h1{
  margin: 50px auto;
  width: 700px;
}

On To The JavaScript!

To get us started – as we are using jQuery – we will throw in the obligatory $(document).ready(); just to ensure that all the code we write only starts executing when the page has loaded.

I use headphones a lot, so I like to just start by setting the volume on both videos to something low. Just to try reduce the risk of me doing something dumb (which is a lot) and blasting my ears.

This is achieved with the prop method and we pass it the attributes of volume and set it to 0.2.

Now its time to create an event listener to listen for keydown events. This is done using the .on() method attached to the whole document and passing it the attribute of “keydown” and then placing our logic inside the callback function. We also give the callback function a parameter which will be passed to it by jQuery.

This will be the information about the event itself. Here I have called this parameter event, but you can call it whatever you want.

So what we have so far is:

1
2
3
4
5
6
7
8
9
10
$(document).ready(function(){

  $('#lightvid').prop('volume', 0.2);
  $('#darkvid').prop('volume', 0.2);

  $(document).on('keydown', function (event) {
    //Some Logic
  });

});

This will trigger every single time any key is pressed. This isn’t quite where we need to be. What we need to do now is add some logic that checks if the key that is pressed.

Like I said above, we have the callback function a parameter which will be given to us by jQuery and will contain the information about the event that triggered the callback. We can use this to run our check for what key was pressed.

1
2
3
if(event.keyCode === 82){
  // What to do when the "r" key is pressed.
}

The keydown event has an attribute called keyCode which is a number that corresponds to the key that was pressed. Here we are checking if that keyCode is 82, which corresponds to lower case r. To see a list of keyCodes take a look at CSS-Tricks.

Now we can specify what we want to happen when our key is pressed. In this case we want to move the currently visible video off the screen and mute it, while unmuting the newly exposed video.

1
2
3
4
5
if(e.keyCode === 82){
  $('#lightvidcont').css('top', -2000 + 'px');
  $('#lightvid').prop('muted', true);
  $('#darkvid').prop('muted', false);
}

The exact reverse process has to then happen when the key is released – when the keyup event is fired.

1
2
3
4
5
6
7
$(document).on('keyup', function(e){
  if(e.keyCode === 82){
    $('#lightvidcont').css('top', 0 + 'px');
    $('#lightvid').prop('muted', false);
    $('#darkvid').prop('muted', true);
  }
});

Yay! We are done.

Kind of. When you press and release your key of choice, it will indeed move the top video on and off the screen. The problem is that if you hit play on the top video, the bottom video remains paused – so you don’t get the wonderful synchronised effect.

So we now have to add a bit of logic that couples the playing and pausing of the two respective videos.

We will start by invoking the trusty .on() method on our video and passing it the event of “play”. Inside the callback function we have many options as to how you can couple the playing of both videos.

Bearing in mind that using $("video").on("play"); will trigger every time either video is played. So what I did to couple them is to simply loop through all videos on the page (only two here) and play them all. The ensures that regardless of which video is played, all videos will couple with it.

1
2
3
4
5
$('video').on('play', function() {
  for(var i = 0; i &lt; 2; i++){
    $('video').get(i).play();
  }
});

This exact same process is repeated for the pause event to couple that and now we are done!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$(document).ready(function(){

  $('#lightvid').prop('volume', 0.2);
  $('#darkvid').prop('volume', 0.2);

  $(document).on('keydown', function (e) {
    if(e.keyCode === 82){
      $('#lightvidcont').css('top', -2000 + 'px');
      $('#lightvid').prop('muted', true);
      $('#darkvid').prop('muted', false);
    }
  });

  $(document).on('keyup', function(e){
    if(e.keyCode === 82){
      $('#lightvidcont').css('top', 0 + 'px');
      $('#lightvid').prop('muted', false);
      $('#darkvid').prop('muted', true);
    }
  });

  $('video').on('pause', function() {
    for(var i = 0; i &lt; 2; i++){
      $('video').get(i).pause();
    }
  });

  $('video').on('play', function() {
    for(var i = 0; i &lt; 2; i++){
      $('video').get(i).play();
    }
  });
});

And that’s all there is to it. Of course, this is not necessarily the best way of doing this, but it is certainly one way of doing it.

Now you can head over to part 2, where we will take this code and clean it up a bit and make it modular and easier to read.

See you there!

Adrian