User Interaction

After finishing the previous section of this tutorial, you should have a working web simulation of Newton’s Cannon, with animated graphics. But the simulation runs just once when the page loads, and the projectile’s initial velocity is hard-coded to a fixed value. Your next task is to hand the control of the simulation over to the user.

1. Add a “Fire!” button.

First let’s add a “Fire!” button to start the simulation. To put a button into your page you can use the HTML input element, like this:

<input type="button" value="Fire!">

Go ahead and insert this code, below the div that contains the image and the canvas, but above the paragraph of text, and check that the button appears. To center the button, put it inside its own div with a style attribute that sets text-align:center. (You could put the button inside a paragraph instead of a div, but it’s best to reserve paragraphs for text.)

Next you need a JavaScript function to call when the button is pressed. This function should initialize the position and velocity variables, then call moveProjectile to get the simulation started:

function fireProjectile() {
    x = 0;
    y = earthRadius + mountainHeight;
    vx = 6000;
    vy = 0;
    moveProjectile();
}

The position and velocity variables still need to be declared at the global level (so their values persist after this function exits), but those declarations can now be shortened to a single line:

var x, y, vx, vy;     // position and velocity

To call fireProjectile when the button is pressed, you can simply set the button’s onclick attribute inside its HTML tag:

<input type="button" value="Fire!" onclick="fireProjectile();">

Now, when you load the page, you can fire the projectile repeatedly. Try it!

But there’s a slight problem: If you impatiently click the Fire! button again, before the projectile has landed, the simulation will restart and will now run twice as fast as before. This is because you’ve called moveProjectile again while another call to moveProjectile is still pending via the most recent setTimeout, so you end up with two calls to moveProjectile either pending or running at any given time. You can fix this bug by storing the result of setTimeout in a variable:

timer = window.setTimeout(moveProjectile, 1000/30);

Declare this timer variable (“var timer;”) at the global level, and then insert the following line at the beginning of fireProjectile:

window.clearTimeout(timer);

2. Add a slider to set the launch speed.

Finally it’s time to let the user adjust the projectile’s initial speed. The best way to do this is with a slider control:

<input type="range" min="0" max="8000" step="100" value="3000">

Insert this tag right after the one for the button, inside the same div, and check that it shows up in your browser. (Support for this HTML5 feature has been slow in coming to certain browsers, most notably Firefox, which didn’t implement it until August 2013.) The attribute meanings should be self-explanatory; I’ve chosen their values based on my personal judgment, which you should feel free to override.

To access the slider’s value from your JavaScript code, you need to set its id attribute:

... type="range" id="speedSlider" min="0" ...

Then declare and initialize a global variable with the same name:

var speedSlider = document.getElementById("speedSlider");

Now you can simply replace the line in fireProjectile that sets vx with the following:

vx = Number(speedSlider.value);

(The Number function forces JavaScript to convert the value from a character string to a number immediately, rather than later when you try to do arithmetic with it. This doesn’t matter here, but I’ve found that it can sometimes dramatically affect performance so I consider it a good habit. The reason why the value is a string to begin with is rooted in the conventions used for all input controls.)

Once again, be sure to test your code after making these changes.

3. Add a numerical readout for the slider.

Unfortunately, the slider doesn’t automatically come with a numerical readout to show the user its value. (Internet Explorer actually does provide a readout, but it has some limitations and in any case, you don’t want to assume that all your users are running Internet Explorer.) Fortunately, you can add a readout pretty easily.

Start by adding the following line of content to your HTML, in between the button and the slider:

Initial speed = <span id="speedReadout">3000</span> m/s

The span element is the in-line version of div; it does nothing inherently to its content, but lets you change the styling or, in this case, assign an id. To access this element from JavaScript, put it into a variable as usual:

var speedReadout = document.getElementById("speedReadout");

Next, define a function that sets the content of the readout to the slider’s value whenever it is called:

function showSpeed() {
    speedReadout.innerHTML = speedSlider.value;
}

Now you can just add calls to this function as attributes inside the slider’s HTML tag:

... value="3000" oninput="showSpeed();" onchange="showSpeed();">

(Why two different attributes? The first, oninput, is supposed to “fire” whenever the user moves the slider’s thumb, while the second, onchange, is supposed to fire only when the thumb is released. But there has been some inconsistency among browsers in implementing these features, so I’m in the habit of using both, just to be safe.)

Just as most web developers frown upon the use of in-line styling via the style attribute, they also tend to frown upon the use of in-line JavaScript via attributes like onclick and onchange. Apparently their view is that all JavaScript code should be segregated in its own separate file, rather than being mixed in with the HTML. That’s actually pretty easy to do, but I frankly don’t see any advantage to it for the types of applications discussed here.

Review

I’ve prepared a User interface reference sheet that summarizes the syntax not only for buttons and sliders, but also for other common types of graphical user-interface features such as checkboxes, drop-down menus, and direct mouse (or touch) events on a canvas. The examples that accompany this tutorial demonstrate each of these interaction mechanisms.

If you look up user-interface controls in a more general HTML reference site or book, you’ll find them in the section on forms. That’s because these controls were originally intended for use on forms that users fill out in order to send information back to web servers. Fortunately, the technologies are sufficiently powerful that we client-side web programmers have successfully co-opted them. Just don’t be surprised if the examples that you find elsewhere are cluttered with extraneous <form> tags and references to “submit” buttons.

Next: Finishing Touches

Back to index

Copyright © 2014, Daniel V. Schroeder