Difference between revisions of "Introduction to JavaScript Fall 2009 Class 4"

esse quam videri
Jump to: navigation, search
m
m
Line 1: Line 1:
[[Introduction_to_JavaScript_Fall_2009|Back to Introduction to JavaScript Fall 2009]]
 
 
 
== Introduction ==
 
== Introduction ==
  
This week I will talk about using some of the concepts I've shown you the last few weeks to actually
+
Last week, I showed you how you can wrap chunks of JavaScript code up into functions that can be reused
start putting together a web page that uses JavaScript. I want to make sure that everyone understands
+
throughout your page. This week, I will explain JavaScript events and show you how you can tie JavaScript
the concepts I've presented so far are making sense to everyone, so the majority of the class will
+
functions to different user events that happen on your page.
be dedicated to building a simple game that tests your knowledge. ''This game will be your assignment for the week,
 
but hopefully you will be able to finish it during class time.''
 
  
Before starting the game, there are a few coding style issues that I'd like to cover.  
+
I will also show you how you can change an element's HTML and even change an element's styles.
  
 
== Coding Style ==
 
== Coding Style ==
  
One thing that I haven't talked about is coding style. Like the English language, JavaScript has it's own  
+
One thing that I haven't talked about yet is coding style. Like the English language, JavaScript has it's own
 
styles that you should follow if you want to make your code readable for others (and, of course, for yourself).
 
styles that you should follow if you want to make your code readable for others (and, of course, for yourself).
  
 
The [https://developer.mozilla.org/en/docs/Javascript_Style_Guide Mozilla JavaScript Style Guide] is a comprehensive
 
The [https://developer.mozilla.org/en/docs/Javascript_Style_Guide Mozilla JavaScript Style Guide] is a comprehensive
 
list of rules that you should try to follow when writing JavaScript. Some of the rules are more important than others,
 
list of rules that you should try to follow when writing JavaScript. Some of the rules are more important than others,
and many of them might not make sense yet for a beginner class. I've listed a few of the rules that apply for this  
+
and many of them might not make sense yet for a beginner class. I've listed a few of the rules that apply for this
 
class so far.
 
class so far.
  
Line 37: Line 33:
 
This name is better because '''name''' tells you what the variable is being used for.
 
This name is better because '''name''' tells you what the variable is being used for.
  
'''Multiple Word Names'''  
+
'''Multiple Word Names'''
  
 
When your variable or function names have multiple words in them, it's JavaScript style to use '''camel case (aka camelCase)'''
 
When your variable or function names have multiple words in them, it's JavaScript style to use '''camel case (aka camelCase)'''
Line 58: Line 54:
  
 
Another important coding style is using proper indentation in your code. Just like indenting the first sentence of a paragraph
 
Another important coding style is using proper indentation in your code. Just like indenting the first sentence of a paragraph
can make text easier to read, indenting your code properly can make it easier to read as well. Typically, when you switch  
+
can make text easier to read, indenting your code properly can make it easier to read as well. Typically, when you switch
to a new ''block'' of code, you should indent 2 spaces.  
+
to a new ''block'' of code, you should indent 2 spaces.
  
 
Remember that, in JavaScript, bocks are sections of code that are surrounded by { curly brackets }. So, every time you're using
 
Remember that, in JavaScript, bocks are sections of code that are surrounded by { curly brackets }. So, every time you're using
 
an opening curly bracket, you should indent 2 spaces. The code example below doesn't use any indentation:
 
an opening curly bracket, you should indent 2 spaces. The code example below doesn't use any indentation:
 
    
 
    
   function alertMatt()
+
   function alertMatt() {
  {
+
   if (name === "Matt") {
   if (name === "Matt")
 
  {
 
 
   alert('hello Matt');
 
   alert('hello Matt');
   }
+
   } else {
  else
+
   alert('You're not Matt!');
  {
 
   alert('You're not Matt!');  
 
 
   }
 
   }
 
   }
 
   }
Line 78: Line 70:
 
Is this code easy to read? How about if it was indented:
 
Is this code easy to read? How about if it was indented:
  
   function alertMatt()
+
   function alertMatt() {
  {
+
    if (name === "Matt") {
      if (name === "Matt")
+
       alert('hello Matt');
       {
+
    } else {
          alert('hello Matt');
+
       alert('You're not Matt!');
      }
+
    }
      else
 
       {
 
          alert('You're not Matt!');  
 
      }
 
 
   }
 
   }
  
Line 94: Line 82:
 
a feature that help you properly indent your code.
 
a feature that help you properly indent your code.
  
== Links ==
+
== Coding Style Links ==
  
 
* [https://developer.mozilla.org/en/docs/Javascript_Style_Guide Mozilla JavaScript Style Guide]  
 
* [https://developer.mozilla.org/en/docs/Javascript_Style_Guide Mozilla JavaScript Style Guide]  
 
* [http://www.chromeexperiments.com/ Chrome Experiments]
 
* [http://www.chromeexperiments.com/ Chrome Experiments]
  
== Activity ==
+
== Finding Elements ==
 +
 
 +
Before we can start capturing events and writing our own code to handle those events, we need to be able to find
 +
the elements that we want to attach event handlers to. Let's say you have a ''div'' on the page and you give it an
 +
id of '''clickableDiv''':
 +
 
 +
  <div id="clickableDiv"></div>
 +
 
 +
If you wanted to find the div and store it in a variable, you would use the '''document.getElementById''' function:
 +
 
 +
  var clickableDiv = document.getElementById('clickableDiv');
 +
 
 +
If the div was found, the function returns the element. Later on, you can use the '''clickableDiv''' variable to
 +
attach events to the element.
 +
 
 +
== Events ==
 +
 
 +
In JavaScript, events are ''raised'' when a user takes some action on the page. For example, a user clicking on
 +
link would ''raise'' the '''onclick''' event on the element that the user clicked on. Without JavaScript, the user
 +
interacts with the page and the browser handles any events that the user might raise. Many times, the browser's
 +
way of handling the events is just fine. Other times, there may be more interesting things that we can do with
 +
JavaScript by capturing the events and handling them in a different way than the browser would handle the events.
 +
 
 +
What kind of ''actions'' from the user are considered events though? It depends on the browser, but, for the most part
 +
there is a core set of events that the majority of browsers will support. So, for example, a user might click on an element
 +
on the page. If the element is a ''button'', the browser will submit a form. If the element is an ''anchor'',
 +
the browser will go to the page that the anchor links to. Or maybe the user just clicked on a ''div'' element.
 +
In that case, the browser probably wouldn't do anything. Not unless we add some JavaScript.
 +
 
 +
=== Setting Events ===
 +
 
 +
So, let's take the div from above:
 +
 +
    var clickableDiv = document.getElementById('clickableDiv');
 +
 
 +
We now have the div stored in a variable called '''clickableDiv'''. Let's say we wanted to have a function that handled
 +
the event that was raised when the user clicked on '''clickableDiv'''. First, we should create a function that will be used
 +
to handle the event. Let's call it '''handleClick'''.
 +
 
 +
  function handleClick() {
 +
      console.log('hello');
 +
  }
 +
 
 +
The function will write message that says "hello" to the Firebug console when it is called. Let's make it so that the function is
 +
called when the user clicks on '''clickableDiv'''. To do this, we'll need to use the '''clickableDiv''' variable that we
 +
the element by adding a '''dot (.)''' after the variable name and then the name of the attribute. In this case, we
 +
want to modify the '''onclick''' attribute of the element, and set it to the name of the new function that we created:
 +
 
 +
  clickableDiv.onclick = handleClick;
 +
 
 +
Notice that we used the clickableDiv variable and we added '''.onclick''' after it and then set it equal to handleClick.
 +
Attributes of elements work like variables, we can set them with the equals sign and even use them for comparisons later on.
 +
 
 +
=== Other Events ===
 +
 
 +
We already set the '''onclick''' event, but there are other events that we could have set as well. For example, if we wanted to
 +
call a function when the user pressed their mouse button down we could add a handler for the '''onmousedown''' function.
 +
When the user released the mouse button, we could capture the '''onmouseup''' function:
 +
 
 +
  clickableDiv.onmousedown = handleMouseDown;
 +
  clickableDiv.onmouseup = handleMouseUp;
 +
 
 +
Or maybe we want to capture the event that happens when the user moves their mouse over an element ('''onmouseover''') or maybe
 +
when a user doubleclicks ('''ondblclick''') their mouse:
 +
 
 +
  clickableDiv.onmouseover = handleMouseOver;
 +
  clickableDiv.ondblclick = handleDoubleClick;
 +
 
 +
There are some events that apply to all elements on the page and other events (like '''onchange''') that apply specifically to
 +
HTML form elements. For a comprehensive list of events and which browsers support each of them, see
 +
[http://www.quirksmode.org/dom/events/index.html here].
 +
 
 +
=== the window.onload Event ===
 +
 
 +
One issue that you will run into when attaching events with JavaScript is that the code to attach your events often runs before
 +
the whole page has actually loaded. This can happen if you include your JavaScript in the ''head'' tag of your page. The browser
 +
will load the JavaScript as soon as it encounters your ''script'' tag, but the rest of the page stil needs to load before you can
 +
start attaching events to elements.
 +
 
 +
A common solution to this problem is to attach an event to the window's '''onload''' event. Every page has a '''window''' object
 +
that represents the current browser window. When the page loads, the browser will raise the window's '''onload''' event, which you
 +
can attach a handler function to.
 +
 
 +
So, let's say you want to run a function called '''setup''' when the page loads. You can attach the '''setup''' function to onload
 +
event like this:
 +
 
 +
  function '''setup'''() {
 +
    // do some setup stuff
 +
  }
 +
 
 +
  '''window.onload''' = '''setup''';
 +
 
 +
Any JavaScript that gets called in the setup function will be called after the page has loaded. So, you should have access to all
 +
of the elements on the page.
 +
 
 +
=== this Keyword ===
 +
 
 +
There is a special keyword in JavaScript that I haven't mentioned yet, and you can start taking advantage of it now that you've
 +
starting to use events. JavaScript has a special keyword called '''this''' that behaves in different ways,
 +
depending on where you use it. It's often confusing, but when used with events, it's fairly straightforward.
 +
Let's say that we used the following code to set an onclick handler for an element:
 +
 
 +
  clickableDiv.onclick = handleClick;
 +
 
 +
  function handleClick() {
 +
    alert(this);
 +
  }
 +
 
 +
The code looks fairly simple: it adds '''handleClick''' as an onclick handler for clickableDiv. '''handleClick''' gets called
 +
when the user clicks on the div and the fuction alerts the user. But what does the user see when they get alerted? If you tried
 +
to run this code, you would probably see something like the text below when you click on the div:
 +
 
 +
  object HTMLDivElement
 +
 
 +
The message you are getting from the browser is telling you that '''this''' is pointing to a div element on the page. But which
 +
div element? It's the same one that clickableDiv points to and the same element that you clicked on. In other words, '''this'''
 +
is a special keyword that points to the element that has the event handler attached to it.
 +
 
 +
The '''this''' keyword can come in handy because it will always give you a reference to the element that raised an event. It's
 +
like having a variable that automatically gets created for the element. That way, you don't need to use document.getElementById
 +
because '''this''' already gives you a reference to the element.
 +
 
 +
== JavaScript Event Links ==
 +
 
 +
In addition to the wiki, you may find these links helpful
 +
 
 +
* [http://www.w3schools.com/js/js_events.asp JavaScript events]
 +
* [http://www.quirksmode.org/js/introevents.html quirksmode introduction to JavaScript events]
 +
* [http://www.quirksmode.org/dom/events/index.html event compatibilty tables]
  
Today, we will be creating a simple number guessing game. The computer will pick a random number and the user will try to guess
+
== Modifying Elements ==
what the number is. If the user guesses too low, the computer will tell them that the number is higher. If the user guesses
 
too high, the computer will tell them that the number is lower. And if the user guesses the number correctly, the computer
 
will tell them that they have won.
 
  
=== Step 1 ===
+
Handling events isn't the only thing you can do with elements on your page. Once you've found an element on the page you can
 +
modify the element in many different ways. Two of the simplest things you can do to an element is change the HTML inside of
 +
an element and change the styles applied to an element.
  
'''get the template'''
+
=== Changing the HTML ===
  
First, download the [http://mattephraim.com/intro_to_javascript/class_4/game.zip template for the game].
+
Once again, we'll use the clickableDiv variable from above. Let's say that we wanted to change the content inside of clickableDiv
I have already created a game board for you. The board has a few elements on it and is styled using CSS.  
+
to say "hello" when the div was clicked on. To do this, we would need to change the '''innerHTML''' of clickableDiv. Nearly all
Open up ''game.html'' and look at the html. There is a form with an ''input'' box for the number that the user will guess.  
+
elements have an '''innerHTML''' attribute that references the HTML inside of the element. We can use '''innerHTML''' to see
There is also a ''button'' that the user can click to send their guess to the computer. Finally, there is a ''div'' at
+
the content inside of the element or we can set '''innerHTML''' to a new value to change the content inside of the element to
the bottom where messages from the computer will be displayed.
+
something new.
  
Notice that 3 of the elements have the '''id''' attribute filled out. We are going to use those ids in our JavaScript.
+
We can create a new handler that uses the '''innerHTML''' attribute of clickableDiv to modify the content:
  
=== Step 2 ===
+
  clickableDiv.onclick = handleClick;
 +
 
 +
  function handleClick() {
 +
    clickableDiv.innerHTML = "hello";
 +
  }
  
'''test the setupGame function'''
+
Setting the innerHTML attribute of clickableDiv changes the content to say "hello". Notice that we used the '''clickableDiv'''
 +
variable to make these changes. There's another way that we could get access to the element that clickableDiv is pointing to.
 +
Remember the '''this''' keyword from above? This version of the '''handleClick''' will do the same thing as the one above,
 +
but it uses the '''this''' to reference the element instead:
  
Open the file called '''behavior.js''' that's in the JavaScript folder. This is the file that contain the program for our
+
  clickableDiv.onclick = handleClick;
game. Notice that I have already created several functions in the file for you. For now, you can ignore the 2 functions
+
 
at the bottom of the page. We will be using these functions later on to make the game work.
+
  function handleClick() {
 +
    this.innerHTML = "hello";
 +
  }
  
The 2 things that I want you to notice are the empty function called '''setupGame''' and the link that says:
+
Whenever possible, use '''this''' to reference the element that raised the event from the user.
  
  window.onload = setupGame;
+
=== Changing the Styles ===
  
This line is important. '''window''' is a variable for the browser window and '''onload''' lets you set a function that will run
+
'''Changing Styles Directly'''
when the browser window loads. So, what this line of code is saying is "call the function '''setupGame''' when the browser
 
window loads". In other words, '''setupGame''' is the name of a function that should run as soon as the page loads.
 
  
Right now '''setupGame''' isn't actually doing anything, because it's empty. Modify the function so that it looks like this
+
An element's HTML isn't the only thing that you can change with JavaScript. You can also change an element's styles as well.
 +
For example, we might want '''clickableDiv''' to have a green border when a user puts their mouse over the div. We might also
 +
want any text inside of it to turn green. We could add a '''onmouseover''' handler that captures the event when a user puts
 +
their mouse over an element. The handler could use the the element's '''style''' attribute to gain access to the element's
 +
css styles and then modify the element's border and font color:
  
   function setupGame()
+
  clickableDiv.onmouseover = handleOver;
  {
+
 
      alert('this is a test');
+
   function handlerOver() {
 +
        this.style.border = "1px solid green";
 +
        this.style.color = "green";
 
   }
 
   }
  
The line we added will simply pop up a test message when the page loads. Reload '''game.html''' page to make sure it works.
+
Every element has a collection of styles that are stored under the style attribute. Most of these styles have the same name
If everything is working you'll see a message popup when you reload the page.
+
as their matching css styles. So, any style that you could set with CSS you can also set with JavaScript by using the
 +
'''style''' attribute. When you modify an element's style, the browser will immediately display your changes.
  
=== Step 3 ===
+
'''Changing the Class Name'''
 +
 
 +
Above, we changed 2 styles on the element. More complex actions might involve a lot more typing and possible a lot more
 +
repetition. How might we have handled the styles above by using CSS? We probably would have created a class that wrapped
 +
up all of the styles:
 +
 
 +
    .highlighted {
 +
        border: 1px solid green;
 +
        color: green;
 +
    }
  
'''setup the random number'''
+
The stylesheet is where styles belong, so it makes the most sense to put the styles there. Once we have the styles, we can
 +
take advantage of them in our JavaScript. Just like every element has style attribute with a collection of styles stored under
 +
it, every HTML element has a '''className''' attribute that can be set by JavaScript. So, instead of setting the CSS styles
 +
manually, the onmouseover handler from above can use '''className''' attribute to set a new class on the element:
  
Also notice that the '''behavior.js''' has a variable called '''randomNumber''' declared at the top. Unfortunately, it
+
   function handlerOver() {
doesn't help us at all, because it's not set to anything yet. Woudn't it would helpful if our '''setupGame''' function
+
        this.className = "highlighted";
set that variable to a random number? I have created a function called '''getRandomNumber''' that returns a random number.
 
Call the function and set the return value equal to the '''randomNumber''' variable.
 
<!--
 
   function setupGame()
 
  {
 
      randomNumber = getRandomNumber();
 
 
   }
 
   }
  
All you need to do is call '''getRandomNumber''' by using the function name with parentheses after it and store the return
+
Changing the style of the element in this way keeps the styles for the page separated from the behavior and keeps your JavaScript
value in the '''randomNumber''' variable.
+
much more compact.
-->
+
 
 +
== Activity ==
 +
 
 +
In this activity we will create a 3 number combination lock. We will use a variable to store the combination that opens the lock.
 +
We will then use HTML, CSS and JavaScript to build an interface that represents the numbers for the lock and the latch for the
 +
lock.
 +
 
 +
A user should be able to enter the combination and open the lock. If the user hasn't entered the combination correctly yet, the
 +
lock shouldn't open. Once the lock has been opened, the user should be able to close it again and have the combination numbers
 +
get reset.
  
=== Step 4 ===
+
=== Step 1 ===
  
'''create a guess function'''
+
Download the template for the lock [http://www.mattephraim.com/intro_to_javascript_fall_2010/class_4/lock.zip here]. I have already created a simple template for the lock
 +
numbers and the lock. Unzip the template and place the whole folder of documents on your desktop.
 +
''Make sure that all of the documents are in the same folders that they were in when they were inside of the zip file.''
 +
We will start by editing the '''behavior.js''' file inside of the JavaScript folder.
  
Earlier, I said we would be using the ids on the html elements in our JavaScript. In this step we will be finding the button
+
=== Step 2 ===
on the page using JavaScript and then setting the button up so that when it's clicked on, it will call a function.
 
  
First, create a function called '''guess'''. This will be the function that we'll use to take a guess at what the number is.
+
I've given a function called '''setup''' to start with. The first thing you need to do is add a line of code that will make
We want this function to get called the user clicks the guess button. For now, put in another test alert, so that we can
+
the function run as soon as the ''page loads''.
test to make sure the function is getting called when we click the button.  
 
 
<!--
 
<!--
  function guess()
+
'''Solution'''
  {
 
      alert('test');
 
  
      return false;
+
  window.onload = setup;
  }
 
 
-->
 
-->
  
'''make the guess button work'''
+
If you've done it correctly, you should see an alert that says "hi" when you reload the page.
  
Now, we can find the guess button and make it so that when you click on the guess button the guess function gets called.
+
=== Step 3 ===
JavaScript has a function called '''document.getElementById()'''. You use this function to find html elements on the page using
 
the id that you gave them. For example, this is how you would find the guess button and store it in a variable:
 
  
  var guessButton = document.getElementById("guess");
+
Notice that there is a list of variables at the top of file. There are 4 variables that will be used to hold on to the elements
 +
on the page. Modify the setup function so that instead of alerting the user it uses the id attribute to find the each of the
 +
elements for numbers and the element for the lock. The function should store the elements in the variables that were created
 +
at the top of the page.
 +
''Make sure that you find the elements inside of the setup function though. The JavaScript won't be able to find the elements until the page has loaded''
 +
 
 +
<!--
 +
'''solution'''
  
The '''document.getElementById()''' function takes in one parameter, which is the id of the element you want to find. If you
+
  function setup() {
look at the html for the project again, you can see that there is only on html tag that has the id '''guess'''. The element
+
    box1 = document.getElementById('number1');
with the id of '''guess''' is the one that the call to '''document.getElementById("guess")''' finds.
+
    box2 = document.getElementById('number2');
 +
    box3 = document.getElementById('number3');
 +
    lock = document.getElementById('lock');
 +
  }
 +
-->
 +
=== Step 4 ===
  
Once the button is found, it gets stored in the variable called '''guessButton'''. We can use the variable to make the button
+
Now that we've found the number elements, we need to find add some functionality so that the numbers do something when a user
do something when it's clicked on. To do this, we'll use something called an event handler. We'll talk about event handlers
+
clicks on them. We will eventually make the numbers change when the user clicks on them, but, first, let's just make a function
more in depth in another class. This week I just want to you set the '''onclick''' handler to your guess function, so that when
+
that handles the clicks and tie the function to the elements.
the button gets clicked, the '''guess''' function gets called.
 
  
You've already seen how to find the guessButton. The next code example shows you how to set the onclick handler on the button
+
Call the function '''changeNumber''' because it will eventually change the number inside of the box. Then use the '''onclick'''
you've found:
+
event handler to tie the function to each of the 3 box elements. Remember that setting '''onclick''' equal to a function
 +
will make it so that the function gets called when the user clicks the element.
  
  guessButton.'''onclick''' = guess;
+
You can test to make sure that your '''changeNumber''' function is getting called correctly by putting a call to alert
 +
inside of it. If you've set everything correctly you should see an alert when you click on one of the boxes.
 +
<!--
 +
'''solution'''
  
The line of code is simply saying: ''when a user clicks '''guessButton''' call the '''guess''' function''. Take the 2 lines
+
  // Sets ups the lock
that find the button and set the handler and put them in your setupGame function, so that they look like this together:
+
  function setup() {
 +
    box1 = document.getElementById('number1');
 +
    box1.onclick = changeNumber;
  
  var guessButton = document.getElementById("guess");
+
    box2 = document.getElementById('number2');
  guessButton.onclick = guess;
+
    box2.onclick = changeNumber;
  
You can also remove the test alert in the '''setupGame''' function. We won't need it anymore. If you've done everything
+
    box3 = document.getElementById('number3');
correctly, you should be able to reload the page and then click on the guess button. When you click on it, you should see an
+
    box3.onclick = changeNumber;
alert box that pops up.
+
  }
  
 +
  function changeNumber() {
 +
    alert('test');
 +
  }
 +
-->
 
=== Step 5 ===
 
=== Step 5 ===
  
'''find the message area'''
+
The next thing we need to do is make the numbers count up when the user clicks on the boxes. The first number in each box
 +
is 0. We will need to find this number and add 1 to it.
  
For the next step, you want to make it so the guess function updates the message area when the button is clicked. For now,
+
First, let's see if we can find the current number. Remember that you can access the HTML of an element by using the '''innerHTML'''
we'll just send a test message to the message area. First, remove the any other code you have in the guess function already.
+
attribute of the element. We used the same onclick handler for all of the elements though. How do we know which element
 
+
to find the HTML for when a user's clicks a box? See if you can modify '''changeNumber''' function so that it alerts the
If you look at the HTML for the page, you'll see that the the message area has an id of '''message-area'''. Update the
+
number inside of the box that was clicked.
guess function so that it finds the element for '''message-area''' and stores it in a variable called '''messageArea'''.
+
''Hint: remember how you can use the '''this''' keyword to find the element that raised the event''.
 
<!--
 
<!--
    var messageArea = document.getElementById("message-area");
+
'''solution'''
 +
  function changeNumber() {
 +
    alert(this.innerHTML);
 +
  }
 
-->
 
-->
 +
=== Step 6 ===
  
'''change the html for the message area'''
+
Now that you know how to get the number from the box you can change the function so that it adds 1 to the number and resets
 +
the HTML for the element to the new number.
  
One you've found the message area and stored it in a variable called '''messageArea''', it's easy to modify the HTML that's
+
The first thing you'll want to do is store the number in a variable called '''currentNumber'''. Then, once you have the number
inside of the message area using JavaScript. Every HTML element allows you to modify the HTML by setting '''innerHTML'''
+
you can add 1 to it. Do you remember a shorthand we used last week to add 1 to a number?
equal to something. For example, this is how you would update the content for '''messageArea''' to say "hello!":
 
  
   messageArea.innerHTML = "hello!";
+
Once you've added 1 to the number, you need to set the HTML of the element so that it matches the new number. Use the
 +
'''innerHTML''' attribute again. This time use it to set the value of the new HTML.
 +
<!--
 +
'''solution'''
 +
 
 +
   function changeNumber() {
 +
      var currentNumber = this.innerHTML;
 +
      currentNumber++;
 +
 
 +
      this.innerHTML = currentNumber;
 +
  }
 +
-->
 +
If things are working correctly the number inside of each box should go up by 1 when you click the box.
  
Update the guess function so that it uses the '''messageArea''' variable to change message area html to say
+
=== Step 7 ===
"the guess button was clicked!".
 
  
If you've done everything correctly, you should see the message area quickly show your message, and then reload the page.  
+
You'll probably notice one problem with the way the numbers work right now: when you click the numbers, they just keep counting
The page is reloading, because the button you're clicking is automatically causing the form to reload the page. The way
+
up. What we really want is for the boxes to count up to 9 and then go back to zero if the user clicks again.  
to fix this is to put '''return false;''' at the end of your function. Now, when you click the button, you'll see the message,
 
but your page won't reload.
 
  
If you change the onclick on any elements that will cause the browser to take action, like a button or a link, you need to
+
Modify the '''changeNumber''' function so that it checks if the current number is ''less than'' 9 and adds 1 to it if it is.
'''return false''' from the function that gets called when you click. This will tell the browser that you don't want the
+
Otherwise, the function should set the number back to 0.  
browser to follow the link or continue with what the button was supposed to do. Don't worry about why this works for now,
 
just know that returning false will let you have full control over the event that you're trying to attach a function to.
 
 
<!--
 
<!--
   function guess()
+
'''solution'''
  {
+
 
      var messageArea = document.getElementById("message-area");
+
   function changeNumber() {
       messageArea.innerHTML = "The guess button was clicked!";
+
    var currentNumber = this.innerHTML;
     
+
    if (currentNumber < 9) {
      return false;
+
      currentNumber++;
 +
    }
 +
    else {
 +
       currentNumber = 0;
 +
    }
 +
 
 +
    this.innerHTML = currentNumber;
 
   }
 
   }
 
-->
 
-->
=== Step 6 ===
+
Now, when you click on the numbers they should count up to 9 and then go back to 0.
  
'''check the user's guess'''
+
=== Step 8 ===
  
The last thing we need to do is have the game check what the user's guess was. I've already created a function that returns
+
The numbers for the combination lock are now working correctly. The next step is to allow user to click the lock image. Add a new
the user's guess for you. It's called '''getUserNumber()''' and when you call it, you get the number that the user entered
+
function called '''checkLock''' that will eventually be used to check if the lock should open. Go back to the setup function
in the textbox before they hit the guess button.  
+
and add an '''onclick''' handler to the lock so that the '''checkLock''' function gets called when user clicks on the lock.
  
'''check if the random number is higher than the user's guess'''
+
Again, you can check to make sure that you function works by adding an alert to the function.
  
We want to give the user a message that tells them if their guess was lower or higher than the random number. We can use
+
<!--
the comparison operators from last week to check the user's number versus the random number that the computer picked.
+
'''solution'''
For example, the following code will check if the number they user guess was higher and show them a message:
 
  
   if (randomNumber > getUserNumber())
+
   lock = document.getElementById('lock');
   {
+
  lock.onclick = checkLock;
      messageArea.innerHTML = "It's higher!";
+
 
 +
   function checkLock() {
 +
    alert('test');
 
   }
 
   }
 
+
-->
The if statement compares the '''randomNumber''' variable to the value from '''getUserNumber()''' using the greater than operator.
+
=== Step 9 ===
It's asking '''is randomNumber greater than getUserNumber's return value?''' And if the answer to that question is true, it's
+
 
setting the HTML for '''messageArea''' to "It's higher!".
+
Now we need to check to make sure the user has correctly entered the combination for the lock and then do something if they have.
 +
The user's combination will be made up of the HTML that is currently inside of each of the elements. You can store the HTML for
 +
all 3 of the box elements in an variable like this:
 +
 
 +
  var combination = box1.innerHTML + box2.innerHTML + box3.innerHTML;
 +
 
 +
The '''combination''' variable should now be a string equal to the HTML for all 3 box elements. So, if the boxes are all set to 3,
 +
'''combination''' will be "333".
  
Right now, the code only checks if the number is higher. You can use another if statement to check if the random number is
+
Write some code that checks if '''combination''' is equal to the '''COMBINATION''' variable that represents the actual combination
lower than the user's number. But, let's use an '''else if''' statement instead, so we can chain a few things together:
+
for the lock. Alert the user if the combinations are equal to each other.
 +
<!--
 +
'''solution'''
  
   if (randomNumber > getUserNumber())
+
   function checkLock() {
  {
+
    var combination = box1.innerHTML + box2.innerHTML + box3.innerHTML;
      messageArea.innerHTML = "It's higher!";
+
    if (combination === COMBINATION) {
 +
      alert('hello');
 +
    }
 
   }
 
   }
   else if (randomNumber < getUserNumber())
+
-->
  {
+
=== Step 10 ===
      messageArea.innerHTML = "It's lower!";
+
 
 +
Instead of alerting the user when the combination is correct, we want to change the lock image to one that represents an unlocked
 +
lock. When you have an image element, you can change the source for the image by modifying the '''src''' attribute. The
 +
'''checkLock''' function handles '''onclick''' for the lock image, which means that '''checkLock''' should have access to the
 +
lock image through the '''this''' keyword. Use '''this''' to change the '''src''' for the image to "images/unlocked.png" when
 +
the user clicks the lock and the combination is correct.
 +
<!--
 +
'''solution'''
 +
   if (combination === COMBINATION) {
 +
    this.src = "images/unlocked.png";
 
   }
 
   }
 +
-->
 +
=== Step 11 ===
  
Now, the code will also check if random number is lower and send the user a message that says "It's lower!" if it is. The code
+
We now have a lock that opens, but there is no way to close the lock once it has been opened. It would be nice if we had the ability
after the '''else if''' will only run if first if statement didn't run.  
+
to lock the lock again.
 +
 
 +
One thing that we should keep track of is whether or not the lock has been unlocked. Add variable to the top of the page called
 +
'''unlocked''' and make sure the variable is set to '''false''' to start with:
 +
 
 +
  var unlocked = false;
 +
 
 +
In the '''checkLock''' function set this new variable equal to true at the same time that you change the lock image. Now we have
 +
a way of keeping track of if the lock is still locked or not.
  
Finally, we can add an extra '''else''' statement that will tell the user that they guessed the number correctly. What would
 
that code look like?
 
 
<!--
 
<!--
  if (randomNumber > getUserNumber())
+
'''solution'''
  {
+
   if (combination === COMBINATION) {
      messageArea.innerHTML = "It's higher!";
+
    this.src = "images/unlocked.png";
   }
+
    unlocked = true;
  else if (randomNumber < getUserNumber())
 
  {
 
      messageArea.innerHTML = "It's lower!";
 
  }
 
  else
 
  {
 
      messageArea.innerHTML = "You got it!";
 
 
   }
 
   }
 
-->
 
-->
 +
=== Step 12 ===
 +
 +
Now that we can check if the lock is locked or not, we can close the lock and reset the combination if the user clicks on a lock
 +
that has already been opened. To do this, we need to modify the '''checkLock''' function so that it first checks if the lock has
 +
already been unclocked. If it's already been unlocked it should alert the user and say "already unlocked!". Otherwise, it should
 +
do what it did before and unlock the lock.
 +
 +
<!--
 +
'''solution'''
 +
if (unlocked) {
 +
unlocked = false;
 +
this.src = "images/locked.png";
 +
} else if (combination === COMBINATION) {
 +
    this.src = "images/unlocked.png";
 +
unlocked = true;
 +
}
 +
-->
 +
=== Step 13 ===
  
=== Assignment ===
+
Finally, let's write a function that resets the lock back to locked and sets all the numbers back to 0. Call the new function
 +
'''reset'''. This new function should modify the innerHTML for box1, box2, and box3, so that they are all equal to 0 again. It
 +
should also modify the '''src''' on the lock element so that it's equal to "images/locked.png" again. And finally, it should
 +
set the '''unclocked''' variable back to false again.
  
Your assignment for this week is to complete the activity above and turn it in by Saturday, September 3rd at 10pm.
+
In the '''checkLock''' function you should call the '''reset''' function if the lock has already been unlocked.
  
[[Category:Introduction to JavaScript Fall 2009]]
+
<!--
 +
'''solution'''
 +
if (unlocked) {
 +
    reset(); // a new function you need to define
 +
} else if (combination === COMBINATION) {
 +
    this.src = "images/unlocked.png";
 +
unlocked = true;
 +
}
 +
 
 +
  // Define this function somewhere
 +
  function reset() {
 +
    unlocked = false;
 +
    lock.src = "images/locked.png";
 +
    box1.innerHTML = "0";
 +
    box2.innerHTML = "0";
 +
    box3.innerHTML = "0";
 +
  }
 +
-->

Revision as of 22:41, 28 September 2010

Introduction

Last week, I showed you how you can wrap chunks of JavaScript code up into functions that can be reused throughout your page. This week, I will explain JavaScript events and show you how you can tie JavaScript functions to different user events that happen on your page.

I will also show you how you can change an element's HTML and even change an element's styles.

Coding Style

One thing that I haven't talked about yet is coding style. Like the English language, JavaScript has it's own styles that you should follow if you want to make your code readable for others (and, of course, for yourself).

The Mozilla JavaScript Style Guide is a comprehensive list of rules that you should try to follow when writing JavaScript. Some of the rules are more important than others, and many of them might not make sense yet for a beginner class. I've listed a few of the rules that apply for this class so far.

Variables and Functions

Clear Naming

Your variables and functions should have names that clearly indicate what the variable is being used for or what the function does. For example, the following variable is being used to store a person's name:

  var n = "Matthew Ephraim";

This is a bad variable name, because n doesn't tell you anything about what the variable is used for, so it might be confusing later on. A much better name would look like this:

  var name = "Matthew Ephraim";

This name is better because name tells you what the variable is being used for.

Multiple Word Names

When your variable or function names have multiple words in them, it's JavaScript style to use camel case (aka camelCase) to indicate that the variable names are multiple words. For example, this variable name is made up of 2 words:

  var professorname = "Matthew Ephraim";

It's 2 words, but it's also difficult to read. It can be made clearer by using camel case:

  var professorName = "Matthew Ephraim";

Notice that the N is capitalized now. This makes it easier to see where the 2 words are separated. Variables names can't have spaces in them, so this is a simple way to still use 2 words in the variable name. Longer variables names with multiple words can be used as well:

  var professorFullName = "Matthew Ephraim"; // professor full name, 3 words
  var firstClassStartTime = "6:30pm"; // first class start time, 4 words

Indentation

Another important coding style is using proper indentation in your code. Just like indenting the first sentence of a paragraph can make text easier to read, indenting your code properly can make it easier to read as well. Typically, when you switch to a new block of code, you should indent 2 spaces.

Remember that, in JavaScript, bocks are sections of code that are surrounded by { curly brackets }. So, every time you're using an opening curly bracket, you should indent 2 spaces. The code example below doesn't use any indentation:

  function alertMatt() {
  if (name === "Matt") {
  alert('hello Matt');
  } else {
  alert('You're not Matt!');
  }
  }

Is this code easy to read? How about if it was indented:

  function alertMatt() {
    if (name === "Matt") {
      alert('hello Matt');
    } else {
      alert('You're not Matt!');
    }
  }

Is this code easier to read? The code that goes with the function is indented several spaces, and the code that's associated with the if statement and with the else statement is also indented several spaces more. Most good text editors should have a feature that help you properly indent your code.

Coding Style Links

Finding Elements

Before we can start capturing events and writing our own code to handle those events, we need to be able to find the elements that we want to attach event handlers to. Let's say you have a div on the page and you give it an id of clickableDiv:

  <div id="clickableDiv"></div>

If you wanted to find the div and store it in a variable, you would use the document.getElementById function:

  var clickableDiv = document.getElementById('clickableDiv');

If the div was found, the function returns the element. Later on, you can use the clickableDiv variable to attach events to the element.

Events

In JavaScript, events are raised when a user takes some action on the page. For example, a user clicking on link would raise the onclick event on the element that the user clicked on. Without JavaScript, the user interacts with the page and the browser handles any events that the user might raise. Many times, the browser's way of handling the events is just fine. Other times, there may be more interesting things that we can do with JavaScript by capturing the events and handling them in a different way than the browser would handle the events.

What kind of actions from the user are considered events though? It depends on the browser, but, for the most part there is a core set of events that the majority of browsers will support. So, for example, a user might click on an element on the page. If the element is a button, the browser will submit a form. If the element is an anchor, the browser will go to the page that the anchor links to. Or maybe the user just clicked on a div element. In that case, the browser probably wouldn't do anything. Not unless we add some JavaScript.

Setting Events

So, let's take the div from above:

   var clickableDiv = document.getElementById('clickableDiv');

We now have the div stored in a variable called clickableDiv. Let's say we wanted to have a function that handled the event that was raised when the user clicked on clickableDiv. First, we should create a function that will be used to handle the event. Let's call it handleClick.

  function handleClick() {
      console.log('hello');
  }

The function will write message that says "hello" to the Firebug console when it is called. Let's make it so that the function is called when the user clicks on clickableDiv. To do this, we'll need to use the clickableDiv variable that we the element by adding a dot (.) after the variable name and then the name of the attribute. In this case, we want to modify the onclick attribute of the element, and set it to the name of the new function that we created:

  clickableDiv.onclick = handleClick;

Notice that we used the clickableDiv variable and we added .onclick after it and then set it equal to handleClick. Attributes of elements work like variables, we can set them with the equals sign and even use them for comparisons later on.

Other Events

We already set the onclick event, but there are other events that we could have set as well. For example, if we wanted to call a function when the user pressed their mouse button down we could add a handler for the onmousedown function. When the user released the mouse button, we could capture the onmouseup function:

  clickableDiv.onmousedown = handleMouseDown;
  clickableDiv.onmouseup = handleMouseUp;

Or maybe we want to capture the event that happens when the user moves their mouse over an element (onmouseover) or maybe when a user doubleclicks (ondblclick) their mouse:

 clickableDiv.onmouseover = handleMouseOver;
 clickableDiv.ondblclick = handleDoubleClick;

There are some events that apply to all elements on the page and other events (like onchange) that apply specifically to HTML form elements. For a comprehensive list of events and which browsers support each of them, see here.

the window.onload Event

One issue that you will run into when attaching events with JavaScript is that the code to attach your events often runs before the whole page has actually loaded. This can happen if you include your JavaScript in the head tag of your page. The browser will load the JavaScript as soon as it encounters your script tag, but the rest of the page stil needs to load before you can start attaching events to elements.

A common solution to this problem is to attach an event to the window's onload event. Every page has a window object that represents the current browser window. When the page loads, the browser will raise the window's onload event, which you can attach a handler function to.

So, let's say you want to run a function called setup when the page loads. You can attach the setup function to onload event like this:

 function setup() {
   // do some setup stuff
 }
 
 window.onload = setup;

Any JavaScript that gets called in the setup function will be called after the page has loaded. So, you should have access to all of the elements on the page.

this Keyword

There is a special keyword in JavaScript that I haven't mentioned yet, and you can start taking advantage of it now that you've starting to use events. JavaScript has a special keyword called this that behaves in different ways, depending on where you use it. It's often confusing, but when used with events, it's fairly straightforward. Let's say that we used the following code to set an onclick handler for an element:

 clickableDiv.onclick = handleClick;
 
 function handleClick() {
   alert(this);
 }
 

The code looks fairly simple: it adds handleClick as an onclick handler for clickableDiv. handleClick gets called when the user clicks on the div and the fuction alerts the user. But what does the user see when they get alerted? If you tried to run this code, you would probably see something like the text below when you click on the div:

  object HTMLDivElement

The message you are getting from the browser is telling you that this is pointing to a div element on the page. But which div element? It's the same one that clickableDiv points to and the same element that you clicked on. In other words, this is a special keyword that points to the element that has the event handler attached to it.

The this keyword can come in handy because it will always give you a reference to the element that raised an event. It's like having a variable that automatically gets created for the element. That way, you don't need to use document.getElementById because this already gives you a reference to the element.

JavaScript Event Links

In addition to the wiki, you may find these links helpful

Modifying Elements

Handling events isn't the only thing you can do with elements on your page. Once you've found an element on the page you can modify the element in many different ways. Two of the simplest things you can do to an element is change the HTML inside of an element and change the styles applied to an element.

Changing the HTML

Once again, we'll use the clickableDiv variable from above. Let's say that we wanted to change the content inside of clickableDiv to say "hello" when the div was clicked on. To do this, we would need to change the innerHTML of clickableDiv. Nearly all elements have an innerHTML attribute that references the HTML inside of the element. We can use innerHTML to see the content inside of the element or we can set innerHTML to a new value to change the content inside of the element to something new.

We can create a new handler that uses the innerHTML attribute of clickableDiv to modify the content:

  clickableDiv.onclick = handleClick;
 
  function handleClick() {
    clickableDiv.innerHTML = "hello";
  }

Setting the innerHTML attribute of clickableDiv changes the content to say "hello". Notice that we used the clickableDiv variable to make these changes. There's another way that we could get access to the element that clickableDiv is pointing to. Remember the this keyword from above? This version of the handleClick will do the same thing as the one above, but it uses the this to reference the element instead:

  clickableDiv.onclick = handleClick;
 
  function handleClick() {
    this.innerHTML = "hello";
  }

Whenever possible, use this to reference the element that raised the event from the user.

Changing the Styles

Changing Styles Directly

An element's HTML isn't the only thing that you can change with JavaScript. You can also change an element's styles as well. For example, we might want clickableDiv to have a green border when a user puts their mouse over the div. We might also want any text inside of it to turn green. We could add a onmouseover handler that captures the event when a user puts their mouse over an element. The handler could use the the element's style attribute to gain access to the element's css styles and then modify the element's border and font color:

  clickableDiv.onmouseover = handleOver;
 
  function handlerOver() {
       this.style.border = "1px solid green";
       this.style.color = "green";
  }

Every element has a collection of styles that are stored under the style attribute. Most of these styles have the same name as their matching css styles. So, any style that you could set with CSS you can also set with JavaScript by using the style attribute. When you modify an element's style, the browser will immediately display your changes.

Changing the Class Name

Above, we changed 2 styles on the element. More complex actions might involve a lot more typing and possible a lot more repetition. How might we have handled the styles above by using CSS? We probably would have created a class that wrapped up all of the styles:

   .highlighted {
       border: 1px solid green;
       color: green;
   }

The stylesheet is where styles belong, so it makes the most sense to put the styles there. Once we have the styles, we can take advantage of them in our JavaScript. Just like every element has style attribute with a collection of styles stored under it, every HTML element has a className attribute that can be set by JavaScript. So, instead of setting the CSS styles manually, the onmouseover handler from above can use className attribute to set a new class on the element:

  function handlerOver() {
       this.className = "highlighted";
  }

Changing the style of the element in this way keeps the styles for the page separated from the behavior and keeps your JavaScript much more compact.

Activity

In this activity we will create a 3 number combination lock. We will use a variable to store the combination that opens the lock. We will then use HTML, CSS and JavaScript to build an interface that represents the numbers for the lock and the latch for the lock.

A user should be able to enter the combination and open the lock. If the user hasn't entered the combination correctly yet, the lock shouldn't open. Once the lock has been opened, the user should be able to close it again and have the combination numbers get reset.

Step 1

Download the template for the lock here. I have already created a simple template for the lock numbers and the lock. Unzip the template and place the whole folder of documents on your desktop. Make sure that all of the documents are in the same folders that they were in when they were inside of the zip file. We will start by editing the behavior.js file inside of the JavaScript folder.

Step 2

I've given a function called setup to start with. The first thing you need to do is add a line of code that will make the function run as soon as the page loads.

If you've done it correctly, you should see an alert that says "hi" when you reload the page.

Step 3

Notice that there is a list of variables at the top of file. There are 4 variables that will be used to hold on to the elements on the page. Modify the setup function so that instead of alerting the user it uses the id attribute to find the each of the elements for numbers and the element for the lock. The function should store the elements in the variables that were created at the top of the page. Make sure that you find the elements inside of the setup function though. The JavaScript won't be able to find the elements until the page has loaded

Step 4

Now that we've found the number elements, we need to find add some functionality so that the numbers do something when a user clicks on them. We will eventually make the numbers change when the user clicks on them, but, first, let's just make a function that handles the clicks and tie the function to the elements.

Call the function changeNumber because it will eventually change the number inside of the box. Then use the onclick event handler to tie the function to each of the 3 box elements. Remember that setting onclick equal to a function will make it so that the function gets called when the user clicks the element.

You can test to make sure that your changeNumber function is getting called correctly by putting a call to alert inside of it. If you've set everything correctly you should see an alert when you click on one of the boxes.

Step 5

The next thing we need to do is make the numbers count up when the user clicks on the boxes. The first number in each box is 0. We will need to find this number and add 1 to it.

First, let's see if we can find the current number. Remember that you can access the HTML of an element by using the innerHTML attribute of the element. We used the same onclick handler for all of the elements though. How do we know which element to find the HTML for when a user's clicks a box? See if you can modify changeNumber function so that it alerts the number inside of the box that was clicked. Hint: remember how you can use the this keyword to find the element that raised the event.

Step 6

Now that you know how to get the number from the box you can change the function so that it adds 1 to the number and resets the HTML for the element to the new number.

The first thing you'll want to do is store the number in a variable called currentNumber. Then, once you have the number you can add 1 to it. Do you remember a shorthand we used last week to add 1 to a number?

Once you've added 1 to the number, you need to set the HTML of the element so that it matches the new number. Use the innerHTML attribute again. This time use it to set the value of the new HTML. If things are working correctly the number inside of each box should go up by 1 when you click the box.

Step 7

You'll probably notice one problem with the way the numbers work right now: when you click the numbers, they just keep counting up. What we really want is for the boxes to count up to 9 and then go back to zero if the user clicks again.

Modify the changeNumber function so that it checks if the current number is less than 9 and adds 1 to it if it is. Otherwise, the function should set the number back to 0. Now, when you click on the numbers they should count up to 9 and then go back to 0.

Step 8

The numbers for the combination lock are now working correctly. The next step is to allow user to click the lock image. Add a new function called checkLock that will eventually be used to check if the lock should open. Go back to the setup function and add an onclick handler to the lock so that the checkLock function gets called when user clicks on the lock.

Again, you can check to make sure that you function works by adding an alert to the function.

Step 9

Now we need to check to make sure the user has correctly entered the combination for the lock and then do something if they have. The user's combination will be made up of the HTML that is currently inside of each of the elements. You can store the HTML for all 3 of the box elements in an variable like this:

  var combination = box1.innerHTML + box2.innerHTML + box3.innerHTML;

The combination variable should now be a string equal to the HTML for all 3 box elements. So, if the boxes are all set to 3, combination will be "333".

Write some code that checks if combination is equal to the COMBINATION variable that represents the actual combination for the lock. Alert the user if the combinations are equal to each other.

Step 10

Instead of alerting the user when the combination is correct, we want to change the lock image to one that represents an unlocked lock. When you have an image element, you can change the source for the image by modifying the src attribute. The checkLock function handles onclick for the lock image, which means that checkLock should have access to the lock image through the this keyword. Use this to change the src for the image to "images/unlocked.png" when the user clicks the lock and the combination is correct.

Step 11

We now have a lock that opens, but there is no way to close the lock once it has been opened. It would be nice if we had the ability to lock the lock again.

One thing that we should keep track of is whether or not the lock has been unlocked. Add variable to the top of the page called unlocked and make sure the variable is set to false to start with:

  var unlocked = false;

In the checkLock function set this new variable equal to true at the same time that you change the lock image. Now we have a way of keeping track of if the lock is still locked or not.

Step 12

Now that we can check if the lock is locked or not, we can close the lock and reset the combination if the user clicks on a lock that has already been opened. To do this, we need to modify the checkLock function so that it first checks if the lock has already been unclocked. If it's already been unlocked it should alert the user and say "already unlocked!". Otherwise, it should do what it did before and unlock the lock.

Step 13

Finally, let's write a function that resets the lock back to locked and sets all the numbers back to 0. Call the new function reset. This new function should modify the innerHTML for box1, box2, and box3, so that they are all equal to 0 again. It should also modify the src on the lock element so that it's equal to "images/locked.png" again. And finally, it should set the unclocked variable back to false again.

In the checkLock function you should call the reset function if the lock has already been unlocked.