This page contains all the at-home reading material for CS 110, not including the textbooks and such. Feel free to search this if you're looking for something and can't find it.
In this course, you will learn three languages, which play different roles in the implementation of a web page:
skinis used.
We will begin with HTML.
An HTML document is composed out of elements that
begin and end with tags. For example, the H2
tag was used to create the header element you see above:
<h2>
HTML Elements and Tags
</h2>
start tag contents end tag
Here's an example of a simple page; it's an excerpt of the CS fun example that we saw in class on the first day.
<!doctype html> <!-- created by Ellen --> <html> <!-- A simple web page illustrating some basic HTML tags --> <head> <title>Fun CS events</title> </head> <body> <h1>Join us for some fun CS department events!</h1> <ul> <li>Spring Cirque du CS <li>Holiday cookie party <li>Faculty-student frisbee game </ul> <h2>Spring Cirque du CS</h2> <p>A celebration of student accomplishments in Computer Science! <strong>Demonstrate your CS110 project!</strong> </p> <p> <img src="cirque1.jpg" alt="circus treats" height="200"> </p> <h2>Outdoor Fun</h2> <p>All levels of skill and experience are welcome!</p> <blockquote> <p>The faculty have always beaten us in the past, but never again! We have some Wellesley Whiptails <br> on our team and we've been practicing hard, so we're gonna kick the faculty's butt this year! <br> <em>-- anonymous CS student</em> </p> </blockquote> </body> </html>
The following are some HTML tags that you can see above. If you forget what a tag does or are looking for a new tag, you can look up tags in an HTML Reference. (Note that we will not be learning all the tags in that reference; we'll learn a useful subset.) Some of the tags we've seen are:
<html>
<head>
<title>
<body>
<h1>
–<h6>
<p>
<br>
<strong>
<em>
<code>
<q>
<ul>
<ol>
<li>
There are several other tags that you have seen, but still need to learn more about.
<a>
<img>
<link>
and <meta>
We'll learn more about these in this reading.
Most elements have an end tag that matches the start tag.
In a few special cases, particularly </p>
and </li>
, the end tag is optional and
may be omitted because the browser can determine it from context.
Some elements (such as <br>
,
<hr>
,
and <img>
) consist only of a start tag
and do not have corresponding end tags or contents.
These are called empty elements.
Tags serve as instructions telling the browser how to display the contents of elements. The browser is an interpreter for HTML code; it reads the HTML code and renders its elements based on rules for each kind of tag.
Multiple tags can be nested: one
fits inside the other like measuring cups or Russian dolls. If we
have two tags, fred
and barney
, they
can be nested like this:
<fred> Region A <barney> Region B </barney> Region C </fred>
The fred
tag applies to all three regions, Region A,
Region B and Region C, while barney
applies only to
Region B that it surrounds. The Region A and Region C
only have the fred
tag apply to them.
When nesting two tags, the inner tag must be closed before the outer tag is closed. Your browser may not enforce this; it may be forgiving of errors, but you can't be sure that every browser will be so forgiving, so always follow this syntactic rule.
We learned that tags always begin with a left angle
bracket <
and close with a right angle bracket
>
. (Remember that the browser doesn't care whether the
tag's name is upper or lower case.) You can provide additional information
within the tag to further specify what it does, using attributes.
Web pages have a required structure, and so there are required
structural tags to mark the different parts. Each page has a HEAD and a
BODY, enclosed by tags with those names. These two parts are contained
within an HTML element. The HEAD contains information about the page, such
as its TITLE, so the HEAD requires the <title>
element.
We'll learn more of these structural tags later.
Headers are just what you think they are: they're just like headings for chapters, sections, subsections and so forth in books, papers and other documents that you're used to. H1 is for things like chapters, H2 for sections, H3 fo subsections, and so forth.
Text tags are used for textual elements, such as creating paragraphs
using the <P>
tag, or emphasizing some text using
the <EM>
tag, and so forth.
If we want to create a bullet list, we can use
the <UL>
tag, which stands for unordered
list
. This tag is just a container: it encloses a set
of list items, each created by the <LI>
tag. Thus, the following creates a list:
<ul> <li>apples <li>bananas <li>coconuts </ul>
If you want the list items to be numbered automatically, you want
an ordered list, which you can get with
the <OL>
tag.
<a>
Tag)A good example of the use of attributes is seen with the anchor
tag, <a>
. To use it as a hyperlink, you have to
specify where the link takes us. For example, to have a link that
says Google
and takes us to www.google.com
, we say:
<a href="http://www.google.com">Google</a>
The href
part is the attribute. For this tag,
since the attribute is almost always required, we can almost think of it
as an a href
tag, but that way of thinking will confuse us
later, so it's best to think of it as two separate ideas. Later, we'll
see other attributes for the <a>
tag.
(Note: we will intersperse exercises like this in the reading. They are solely for your benefit. They are typically short and may help with understanding the concepts and techniques. They are not graded. We highly recommend that you make time for them.)
To start this exercise, click on this link to JSFiddle. JSFiddle is an extremely
popular sandbox
that allows you to play with HTML, CSS and JS
right in your browser, without having to launch an editor (such as
TextWrangler), save files to your server, or any other such
infrastructure. We'll use it a lot in this course.
Copy/paste the following code into the HTML box of the JSFiddle. Then click the "Run" menu item to see the result.
<p>This is the text on my little page.</p> <p>This is some additional, very boring, text on my page.</p>
Then add an h1
header and an h2
header
to the page.
Now put in a link to your favorite website. Here are some of ours. (To see the URL where they go hover over a link and look at the left-bottom bar of your browser.)
title
tagThe contents of the title
tag doesn't appear on the body
of the web page (though it often appears at the top of the window in the
title bar). It is, however, very important for two reasons:
For both those reasons, a page title like about us
or contact
is often not helpful. It's a good idea to put
something more descriptive in the title, such as About CS 110
or CS 110 staff contact information.
HereLink
Once, it was very popular on the web to have links like this:
It seemed so clever and intuitive, making the clickable text be the word "here." There are two big problems with this, though:
So what do you do instead? Just wrap the link tags around important words:
Accessibility is very important in CS 110, so keep that in mind.
In the head
element of each CS110 lecture page, you'll see
some HTML that looks something like:
<link href="http://cs.wellesley.edu/~cs110/cs110-main-style.css" rel="stylesheet" type="text/css">
Like the anchor tag, the <link>
tag also has an
href
attribute, and it also links one web page with another.
However, it has a different purpose. Instead of producing a clickable
link, the <link>
tag tells the browser that there is
some additional information about this page located in a different file.
The href
attribute of the <link>
tag tells
the browser where to find the other file. The href
attribute
contains a URL, which we'll learn about later.
The <link>
tag contains other attributes depending
on the purpose of the connection. In our case, it contains:
rel
attribute that says what the RELationship
the other file has to this one. We use it to specify a
style sheet, which says how tags should be formatted.
type
attribute that says what kind of stuff the
other file contains. In our case, we say that the file contains
text/css
.
type
is not
required anymore. The reason is that
CSS is declared the default style for HTML5. However, you might see type
in our older examples and in many pages on the Web. It is not an error to use it,
but it's not required anymore.
The <link>
tag can be used for a variety of
purposes, but most current browsers only use it for style sheets.
At this point, the <link>
tag is still a mystery.
We've told you what it's for, and something about its syntax, but not what
goes in the other file. We'll learn more about the <link>
tag, style sheets and CSS in later lectures.
Modify the JSFiddle from the previous exercise by adding the
following link
element near the top. (You'll get a minor
complaint from JSFiddle, but you can ignore it.)
<link href="https://cs.wellesley.edu/~cs110/cs110-main-style.css" rel="stylesheet">
How does this affect the appearance of the document?
In the modified document, how can you change the appearance of an unvisited hyperlink from blue text to text that is large and green? (Hint: use a header tag.)
We can now generalize start tag syntax to include any number of attributes, as follows:
<tag attr1 = "value1" attr2 = "value2" ... attrN = "valueN"> contents </tag>
Browsers differ on how nit-picky they are about attributes. Many will let you get away with omitting the quotation marks when the value is a single solid word (no spaces in the value). Others will complain if you have line-breaks in your attributes. In general, it's best to comply with the strictest syntax rules, so that your site will work on the most browsers.
Note that some versions of HTML, namely XHTML, require attributes to be in quotation marks. We're using HTML5, which is much more liberal, and will let you omit quotation marks unless the value contains a space, a line break, grave accent (`), equals sign (=), less than sign (<), greater than sign (>), quote (") or apostrophe ('). In short, if there's anything confusing in the value, use quotation marks.
One thing that we all want to do with our web pages is add pictures.
Because the picture file is a separate file, we have to link to it, just
like the href
attribute of the anchor
(<a>
) tag.
Once you have an image file, say small_weasel.jpg
you can
use it on your web page like this:
<img src = "small_weasel.jpg" alt = "a small weasel">
with the following result:
Of course, this only works if the server can find the image file.
The src
attribute must be
the URL of the image file. It can be an
absolute URL or a relative URL. What did we use here?
When the browser asks for this page, the server sends it back and it also finds and sends back any image files that the page references. If the server doesn't find the file (or the file is corrupted in some way), the browser will show this:
Depending on your browser, you may see a broken-image icon above, the
alt
text, or possibly nothing at all.
Modify the JSfiddle to display an image of the weasel. (Of course, you could replace this image by any other image you'd like!) Note that in this case, you must use an absolute URL for the IMG, namely:
http://cs.wellesley.edu/~cs110/reading/small_weasel.jpg
Add a title
attribute with value "small weasel image"
to the image. What does this do?
You noticed that we added an ALT attribute to the IMG tag that is a
small piece of text that can be used in place of the image in certain
circumstances. The ALT attribute is an important part of the HTML
standard. Perhaps its most important use supports
accessibility. Unfortunately, not everyone has good enough
vision to see the images that we use in our websites, but that
doesn't mean they can't and don't use the Web. Instead, they
(typically) have software that reads a web page to them, including
links. When the software gets to an IMG tag, it reads the
ALT text. If there is no ALT text, it may read the SRC attribute,
hoping there's a hint there, but all too often the SRC attribute is
something like "../images/DCN87372.jpg"
and the visually
impaired web user is left to guess.
Therefore, you should always include a brief, useful value for the ALT attribute. If your page is an image gallery, then your ALT text could be a description of the image. However, describing the image is not, in general, the idea. For example, if the image is a link whose target is made clear by the image, then the ALT text should say something like, "Link to ..." so the user will know what to do with it. The sole exception is for images that are just used for formatting, such as blank pictures that fill areas or colorful bullets for bullet lists. In those cases, in fact, it's better to include an ALT attribute that is empty, so that the user doesn't have to listen to the SRC attribute being read. In both cases, the text should be useful for someone who wants to use your site but isn't sighted. It helps to turn off images and view your site to check.
Furthermore, you should avoid having critical information on your website conveyed only in images. There may be times when it is unavoidable, but to the extent that it is possible, we want our websites to be easily usable by all people, including the blind and visually impaired.
Accessibility is important in modern society. We build ramps as well as stairs, we put cutouts in curbs, and we allocate parking spaces for the handicapped. Indeed, most federal and state government websites are legally required to be accessible, and ALT attributes are just one part of that.
In this class, we expect you to always use the ALT attribute. If you find an image or an example where we've forgotten to use one, please bring it to our attention.
For more information, you can read the following
If you want to display a bunch of pictures, the web page appears neater if the pictures align well. You can align them vertically if they all have the same width, or horizontally if they all have the same height. For example, the following three pictures are all 150 pixels high.
Regardless of the actual dimensions of an image, the browser will squeeze it into a set size if requested. You can do this with two new attributes, namely HEIGHT and WIDTH:
<img src="..." alt="..." height="height-goes-here" width="width-goes-here">
Replace the "height-goes-here" and "width-goes-here" with integers specified in pixels, which we'll discuss later. If both width and height are specified, both will be obeyed, but you have to be careful with that. Suppose the original image is 160x240: taller than it is wide. Technically, the ratio of the width to the height is called the aspect ratio. The Eiffel Tower picture has an aspect ratio of 160:240 or 2:3. If you set the height and width so that they don't have the same aspect ratio, the picture will look distorted. Here is the Eiffel Tower with the wrong aspect ratio:
<img src="eiffel-tower.jpeg" alt="the Eiffel Tower" width="300" height="150">
If you use either the HEIGHT or the WIDTH attributes, but not both, the browser will usually calculate the other attribute so that the aspect ratio is preserved. Thus, the picture will have either the width or height you want, but will not be distorted. That's how we did that row of pictures above.
Modify the JSfiddle to display an image of the Eiffel tower next to the weasel so that both images have the same height. You'll have to use an absolute URL for both images:
http://cs.wellesley.edu/~cs110/reading/small_weasel.jpg http://cs.wellesley.edu/~cs110/reading/HTML-files/eiffel-tower.jpeg
From the very first computer program, programmers have needed to
leave notes
in the code to help themselves and others understand
what's going on or what the code's purpose is. These notes are called
comments. Comments are a part of the program text (they're
not written separately, because then, well, they'd get separated),
but they are ignored by the computer. Comments aren't about what someone
can discover by reading the code, but should cover the background context
of the code, or its goal.
Because it's important to get in the habit of putting comments in your HTML code, we will require comments in this course. At this point, you won't have a lot to say, and that's fine. You will start by labeling each file with its name, your name, the date, and any sources you consulted (such as the source code of other web pages). Think of this as signing your work. Later, when you're designing a website with many coordinated pages, you can use comments on a page to talk about how it fits into the overall plan.
The HTML comment syntax is a little odd-looking. Here's an example:
<!-- I can say anything I want in a comment. -->
The syntax starts with a left angle bracket <
then an
exclamation point and two hyphens, then the comment (anything you
want) and ends with two hyphens and a right angle bracket >
.
Ah, but you object that your web page looks ugly without centering, font changes, colors, and so forth. That may be; we're not going to try to contradict your aesthetic sense. However, for the first week of this course, we don't want to confuse anyone by introducing style sheets right away. So, we ask you to be patient. We will get to style sheets very soon.
In the meantime, consider the fact that many visitors to your site might not gain any advantage from style sheets anyway, because they are visually impaired, or because they are using an old or alternative browser that doesn't honor style sheets. For such users, the most important thing is the content and having that content well-structured and clearly conveyed. So, until we get to style sheets, consider that you are designing your website with accessibility in mind.
An HTML document may contain many different kinds of errors that prevent it from rendering as you expect. Errors in code are known as bugs, and the process of finding and correcting such errors is called debugging.
Here are some common types of HTML bugs:
<tilte>
or <break>
.
<em>important<em>
<em important</em>
<h1>CS110 HTML Coding</h2>
<h1><em>CS110 HTML Coding</h1><em>
<img source="logo.jpg" hieght=100>
<img src="logo.jpg height=100>
Copy the following buggy HTML code into a JSfiddle and debug the code until it looks like you think it's intended to look.
<h1>A Buggy HTML File</h1> <p> This HTML file contains several <em>bugs</em> (i.e., errors). <br> Can you <em>debug</em> them (i.e., find and fix them)? <p>Here's an unordered list with three items: <ul> <li>A <strong>strong</strong> item.</li> <li>An ordered sublist with two items: <ol> <p> A CAPITALIZED item <li> An <a href="http://www.wellesley.edu"> <em>italic link</a></em> <li> A final <code>item<code>, using code font. </ul> <p>Here's <a href="http://cs.wellesley.edu another link</a>
How can you be sure you've followed every nit-picky rule that the HTML
standards committee devised? (The standards committee is
the World Wide Web Consortium
or
W3C.) Even if you have memorized all the rules, checking a page would be
tedious and error-prone – perfect for a computer! Fortunately, the
W3C created an HTML validator. You
can validate by supplying a URL, by uploading a file, or even
copy/pasting in some HTML. Visit that page and try validating
your little.html
file,
debugged buggy.html
file,
or even the file for this lecture.
An HTML validator is an excellent tool to help you debug your HTML code.
Validation also helps with accessibility. One important aspect of accessibility is having the proper HTML syntax for each page in your site. Visitors with accessibility needs will use the alternative browsers and screen readers, and that software will be aided by syntactically correct HTML. Read the following for a longer discussion of why to validate your HTML pages.
Throughout the semester, if you need to validate a web page, you can find the HTML validator and others in the validators section of the CS110 documentation page.
If you haven't already, try validating little.html
.
You'll see that it doesn't validate. The reasons are slightly technical,
so bear with us, but you'll see how to make your own documents valid.
There are several different, incompatible HTML versions, and so a
document that is valid HTML 3.2 is invalid HTMl 4.01 and vice versa.
Neither would be valid XHTML 1.0. So, the validator needs to know what
version of HTML you're using. More importantly, the browser or screen
reader or other software needs to know what syntax to expect.
Therefore, the first thing any web page needs to do is announce the
syntax it's using. This is done with a special DOCTYPE
(for document type
) tag.
In this course, we're using HTML5. (Your book describes HTML 4.01 (and also XHTML), but HTML 4.01 is essentially a subset of HTML5, so nothing they tell you will be incorrect.) Fortunately, the doctype for HTML5 is very simple and easy, certainly compared to HTML 4.01. It looks like this:
<!doctype html>
We urge you just to copy/paste that tag to your own pages and don't worry about it further.
Note: because HTML5 is still pretty new, the validator at W3C will give you a warning that the result is "experimental."
A browser needs to know what characters (letters, numbers,
and punctuation) are in the HTML file. Are they western
European
characters, or Russian, Greek, Sanskrit, Korean, Japanese,
Chinese, or !Kung
or something else? This information is called the character set
or charset for short. For our purposes, we can use something
called UTF-8
.
To do so, we put a <meta>
tag in
the <head>
of our document. It looks like this:
<meta charset="utf-8">
Again, we urge you just to copy/paste that code and don't worry about it.
Once you get your page to validate, you can put some HTML code on your
page to give it a seal of approval
, declaring that it is valid (and
what standard it meets). You will see in lab examples of this strategy.
The very cool thing about this icon is that it is clickable, and clicking it will cause the validator to process your page again. Thus, you can modify your page, upload the changes, and click the icon to re-validate it, making validation very easy. In fact, we suggest that you put the icon on your page before it's valid, and use it during your debugging process.
The snippet of code is just the following, so go ahead and copy/paste it into your pages. The code doesn't use anything we don't know, so read it!
<p> <a href="http://validator.w3.org/check?uri=referer"> <img src="http://cs.wellesley.edu/~cs110/Icons/valid-html5v2.png" alt="Valid HTML 5" title="Valid HTML 5" height="31" width="88"> </a> </p>
The preceding requirements result in a kind of boilerplate
that
you'll need for all your web pages. Feel free to copy/paste the
following to begin all your HTML pages, or to use
this template.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="style.css"> <meta name=author content="Your name here"> <title>title here</title> </head> <body> <p> <a href="http://validator.w3.org/check?uri=referer"> <img src="http://cs.wellesley.edu/~cs110/Icons/valid-html5v2.png" alt="Valid HTML 5" title="Valid HTML 5" height="31" width="88"> </a> </p> </body> </html>
This section introduces the DIV tag. The DIV tag is a generic
container tag, which doesn't make a lot of sense yet, but will in the
next few readings. To motivate the DIV tag, we'll talk a little about
CSS, but not in a complete or definitive way, so bear with us on
that. We'll get to CSS very soon.
CSS (Cascading Style Sheets) is the style language for web pages. Here is one way (not even the best way) to put a green box around a paragraph:
<p style="border: 3px solid green">Success makes so many people hate you... — Marilyn Monroe</p>
Here it is in action:
Success makes so many people hate you... — Marilyn Monroe
So far, so good. If we wanted to put a box around two paragraphs, we would need an element that contains both those paragraphs. It turns out that HTML paragraphs can't contain other HTML paragraphs, which makes sense because normal everyday paragraphs don't contain other paragraphs.
None of the other HTML elements really work either, so to handle this
situation, the World Wide Web HTML standards committee created
the <DIV>
tag, standing for a division
of your
document. A DIV can contain anything (paragraphs, lists, headers, and even
other DIVs). Consequently, DIV is very useful and is used a lot. A more
real-world example is a putting a set of links into a DIV and positioning
that to be a navigation area, or putting a set of paragraphs in a DIV to
create a set of posts. We will use it a lot in CS 110, though we will
also use some newer tags that are intended to reduce the over-use of DIV.
As we've said, HTML was designed to structure the content of a web
page. That explains the existence of tags like <p>
,
<h1>
, <ol>
, etc. However, when web
developers started creating pages with a lot of content, it became clear
that to make better use of the available screen space, a way to organize
the page content in bigger chunks was needed. Then, CSS could be used to
arrange their position on the page. Therefore, the
tag <div>
was born (short for division),
which is currently the most used (and overused) tag in every
webpage. While this seemed to have solved the page layout problem, HTML
code became difficult to understand, other computer programs
(e.g. search engines) couldn't make sense of all divs in a page, if they
wanted to use the organization of the page for inferring the meaning of
the content.
HTML5 introduced a series of new tags that have meaningful names and can be used universally to express what the content is about, beyond the existing simple tags. Additionally, to make the pages more alive with different kinds of content, several new tags that allow content to be embedded in a page were also added. In the following, we will give a short summary of some of these tags. Try to make use of them in your pages. They will make your code better and more readable to the programs of the future.
Here is a list of new HTML5 tags that are known as semantic tags,
because their names have specific meaning. Click on the info
icon
() for more
information about each one.
Given all the tags listed above, along with DIV, you might feel bewildered as to which one to use. Here is a helpful HTML5 sectioning flowchart from html5doctor.com. Click on the image to see a larger version:
Changing the web language standards goes through a lengthy process spearheaded by the W3C consortium. The vendors of the browsers need often many years to implement all the changes. Therefore, not all new HTML5 are currently supported by all browsers. The info link in the table gives information about which browsers currently support the tag.
HTML5 also introduces more meaningful tags to support embedding media resources directly in the page, allowing for a more rich experience in the browser. Below we show four such tags in action.
The tags in action:
Here is how to use these two tags
<figure> <img src="images/imageName.png" alt="image description" > <figcaption>A caption for the image</figcaption> </figure>
For more examples and explanation for this pair of tags read this article by the HTML5Doctor website.
Here is an example of embedding video directly on your page:
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" width="300" id="bunny_video" >
The attributes controls
and autoplay
are called boolean attributes. A tag either has them or
doesn't have them. Here, we've omitted autoplay, so that the
page won't make noise when you load it.
Here is an example of embedding audio directly on your page:
<audio controls> <source src="http://www.jingle.org/britsungmont.mp3" type="audio/mpeg"> Your browser does not support the audio element. </audio>
Often you want simply to try out new elements or their combination, without having to write a complete HTML document. We have already seen how the documentation for the HTML tags by the W3Schools website provides an editing space where to experiment with tags. However, there are many more tools on the web that allow you to do the same, for all three languages at once: HTML, CSS, and Javascript.
One tool that we will be using during lecture time for experimentation is
jsfiddle.
Here is a screenshot from trying out the <video>
tag directly in its workspace.
http://
cs.wellesley.edu/
~cs110/lectures/L02/
URLs.html
protocol server.domain path filename
let's break it down in its components:
http://
This is the protocol, which is almost always
HTTP, the HyperText Transfer Protocol. You'll also occasionally
see FTP, the File Transfer Protocol; HTTPS, the secure version of
HTTP; and others.cs.wellesley.edu/
This is the server: a
machine connected to the internet that hands out files to anyone
who asks. cs
is the name of the machine;
wellesley.edu
is
the domain. The name of the server combined with the domain must
be unique in the world. In fact, the domain must
be unique in the world, and the server name must be unique within
the domain.~cs110/lectures/L02/
This is the
path: a sequence of directories (AKA folders), separated by
slashes, that indicates where on the server the file is located.
In this path, there's a tilde (~
), which means to start at the
public_html
directory under the home
directory of the cs110
account. We'll always be using
that kind of URL in this class, so keep this special rule in mind.URLs.html
This is the file that is being requested.Because URLs can specify files on many different kinds of computers, you should stick to a lowest common denominator when naming your directories and files, to avoid confusing machines and browsers. The lowest common denominator means:
cats and dogs.html
you're much safer
with cats-and-dogs.html
or
cats_and_dogs.html
Furthermore, be careful with upper and lower case. To your eyes,
cats.html
and Cats.html
may seem like the
same filename, but to a computer, they're as different as
cats.html
and dogs.html
. The same rule is true
of directory names: they are case-sensitive. Many people like to stick to
all lower-case, just so they don't have to remember. .
Use a file transfer program (such as Fetch or WinSCP) to create a path of directories in your CS account, and then create a simple web page that just says “hello world” and put it in the bottommost directory. Construct the URL to the page and then try to view the page with your browser.
Certain parts of the end of an absolute URL can be omitted. For
example, the filename can be omitted and (usually) defaults to a file
named index.html
in the specified directory (for an example, see
our course homepage). Similarly, the
path can be omitted, in which case the page is in the top
directory of
the web server. However, the protocol and server must be
specified. You can't omit those. If a URL has a protocol, it's an
absolute URL, otherwise it'll be treated as a
relative URL.
As you know, most operating systems (including Mac, Windows, Unix, iOS, Android, etc.),
organize the contents of a computer's hard drive into
folders, also called directories. Folders and files form
a tree
structure, because of the ability of directories to contain
other directories.
Here's an example:
For a website about Joss Whedon, we might have a folder about his Buffy the Vampire Slayer TV show, another about the TV show Angel, and a document about his comics. These might all be in a top-level folder calledJoss. (The top-level folder is also called the root folder.) The folders about the TV shows might each have a sub-folder calledcast. And so on.
While it's easy to conceptualize a tree of folders and files, also called a directory tree, there are many different ways to draw it. Here are several ways of depicting the Joss directory tree described above that you will see in this course:
For example, we can see that buffy.html
and willow.html
are both
files in the same directory, the one called cast
, which is, in turn, one
of three things in the directory called Buffy
, which is in the directory
called Joss
. Joss contains two directories (Buffy and Angel) and a file
called comics.html
and so on. Take a minute to be sure you understand
the relationships among the directories and files. Notice that there
are two files named angel.html
; this is okay, because they
are in different directories.
We can use these relationships to form a shorthand way of specifying a URL. This is called a relative URL, because we specify the location of a file relative to a known file. Here are the basic rules:
buffy.html
linking it to willow.html
, we can say:
<a href="willow.html">her best friend Willow</a>
mythology.html
and we want to give a link to the file
angel.html
, we can say:
<a href="cast/angel.html">Angel, the vampire with a soul</a>
upthe tree. To go up, we write
../
. That's dot, dot, slash.For example, if we are in the file
host.html
and we want to make a link to
season3.html,
we can say:
<a href="../season3.html">the Host returned in season 3</a>
..
is tricky: to go up, we write
..
; while to go down we have to indicate which subdirectory
we mean. In either case, we separate these up/down steps with a
slash. Here's another example: suppose we want to put a link
from buffy.html
to comics.html
. In this case,
we can write:
<a href="../../comics.html">another layer in the comics</a>
Create some additional directories in the public_html
folder of
your CS account and practice
with the relative URLs. Relative URLs will make your lives much
easier when you produce different versions and when you deliver your
website to your client. Try the equivalent of:
plot.html
to comics.html
season3.html
to plot.html
gunn.html
to willow.html
The model above establishes a one-to-one correspondence between a URL and a file (web page) somewhere in the vast Internet. Sometimes, however, you want to address (point to) a particular location within a page. Such locations are called fragments.
While fragments can be used in many ways, one common use is to have
a table of contents
at the top of a page, allowing readers to skip
down to the particular section they are interested in. An example of
such a table of contents is at the left side of these notes.
Fragments are addressed by extending the URL by adding a #
character
and the id attribute
of the fragment (the HTML element that refers to the
fragment). Thus, we have:
http://
cs.wellesley.edu/
~cs110/lectures/L03-html/
URLs.html#
relative
protocol server.domain path filename fragment id
One key step is marking the destination of the URL,
by naming the fragment, just as we name a file or folder. This
is done with the id
attribute, which can be applied to
every HTML tag. We typically add it to the header tag (such
as h2
) of the section we want to skip to. For example:
<h2 id="relative">Relative URLs</h2>
Given the code above, we can then point to that section from any web
page anywhere, using an absolute URL, like the one in the table above.
We can also use fragments with relative URLs. For example, to point to
that section from another file in the same directory, we can just give
the filename and the fragment name: URLs.html#relative
. In
fact, if we want to point to it from within the same file, we can omit
the filename and the relative URL becomes just #relative
.
You can see examples of that kind of URL in the table of contents on the
sidebar.
Let's look now at some of the standard navigational structures of web pages. As the name suggests, a site's navigational structure describes the paths of navigation through the site. When thinking about these structures, keep in mind that while some sites consist of only a single page, most "real" websites consist of many pages. In most websites with more than just a few pages, you will see a combination of several of these structures.
Your project should consist of 8-10 pages. You should make sure to discuss with your partner how your web pages are linked to make navigation easy and logical for visitors. In the project design phase you are required to describe the interconnection of all the pages on your site and provide a brief discussion about the site's navigation structure using the terminology learned today.
When HTML was first introduced, there were a number of tags whose purpose was just for formatting: for example, tags that center or switch to an italic font. With HTML version 4.0, such tags were made obsolete (though most browsers still support them), in favor of a formatting technique called Cascading Style Sheets.
When a browser is displaying a page, there is a step of reading a
tag, such as <em>
or <h1>
and
then determining what the results are to look like. (This is
sometimes called rendering a page.) Each browser has a default
way of rendering a tag. For example, most browsers will render the
<em>
tag by switching to an italic version of the current
font. Most browsers will render an <h1>
tag by switching
to a very large version of the current font. There are several important points
to make about this:
<blockquote>
in Safari
indents by a different amount than Internet Explorer does.
<em>
tag to look red, you can do that. The style of tags
is determined by something called CSS, for Cascading Style
Sheets.
You can specify the style of a tag using a style sheet. These style sheets can be put in (at least) three different places:
<head>
of a document, using the special
tag <style>
.
<link>
tag. We discussed this link in the
HTML lecture.
Suppose that we always want <em>
to be red.
(Be aware of accessibility.)
style
attribute of the tag to set the color
property to have the value red and the
background-color
property to have the value white.
(You should be reminded of the attribute/value idea in tags, but
the syntax is different. With CSS, property/value pairs are
separated by semi-colons, and the property ends with a colon.)
Note that you should always pick both colors, in
case the user's browser has specified a background color that
matches your foreground color. Here's the example:
this is an <em style="color: red; background-color: white">important</em>
word
which results in:this is an important word
<style>
tag in the <head>
of your
document to specify the style for the <em>
tag once and
for all:
<style> em { color: red; background-color: white; } </style>
In your document (that is, in the tags enclosed by <body>
),
you just use the <em>
tag normally, and it will be rendered as red.
<style>
tag. Instead, you
reference the file using the <link>
tag in the
<head>
, the same place you would use the
<style>
tag. Suppose we create a file called
my-style.css
that contains the following:
em { color: red; background: white; }
and we reference it in the <head>
like this:
<link rel="stylesheet" href="my-style.css">
As with document-level style sheets, we can just use the <em>
tag
normally from then on.
The Head First HTML and CSS book has a good graphic for explaining the syntax of a CSS rule:
There are several facets to the syntax of a CSS rule:
color
) are followed by
a colon, then at least one space, then the value of the property (such
as red
). If you
want to specify more than one property/value pair, separate them with
semi-colons. If there's only one property/value pair, the semi-colon
is optional. (Many people put it in just in case they add another
property/value pair later.) The order of the style properties does
not matter.
style
attribute in the HTML tag. Remember to enclose
the style properties within quotation marks.
p
or
em
) that you are modifying goes first, then an
opening brace, then the style properties, then a closing brace.
In a CSS rule, the element to which we apply the style is known as the
selector. All tags can be selectors. However,
we will see more ways to define selectors in the forthcoming material.
Spaces and line breaks are ignored, so feel free to format this in
a visually pleasing way. As always, clear and consistent
indentation is very helpful for any reader of your styles.
<style>
tag, while external style sheets always omit
the <style>
tag.
<style>
tags. You'll have nothing in angle
brackets in an external CSS file.
/*
to
begin and */
to end. Thus, for an external style
sheet, you might have:
/* Written by the CS110 Faculty
Specifies a CSS Rule for the element <em>
*/
em {
color: magenta;
background-color: white;
}
Now that you know how to write style sheets, let us see some properties that you can use in these styles. One can style an element over many aspects:
In the following, we give several examples of properties that are very useful for styling elements. For pedagogical purposes, we're not providing the code as text that can be easily copied and pasted somewhere else. We believe that by typing the instructions on your own, you will learn the names and values of properties, as well as potential errors that occur when one types. We strongly suggest that you use jsfiddle to try out all these examples. Before entering the code, read our questions and try to predict the answer without running the code. Then check if you were right. To use jsfiddle, enter the HTML code in the HTML box, the CSS code in the CSS box, and then click the button Run. There is no need to write a whole HTML file, only the snippets shown in the examples. Also, to see the effect of styling, the text in HTML elements doesn't have to be meaningful as in our examples.
In the rendered result, is the background color blue? Why not? What about the color of the
text nested in <em>
? Why it is not white like the rest? What does this tell
about the order in which rules are applied?
Try first to predict how this code will be rendered. What is the effect of each rule on every element?
How will the <em>
nested in <li>
look like? Then, enter this
code in jsfiddle to see whether you were right in your predictions.
CSS has cascading
in the title because there's a
strict hierarchy that determines which specification applies
if the style of a particular tag is set in more than one
place. Suppose you specified the <em>
tag
as (1) green in an external style sheet, (2) blue in a
document-level style sheet and (3) red in an inline style.
Which wins?
The simple answer is that the closest specification wins. This makes sense, because you can use the more local specifications to override the more distant, global specifications. So, inline style attributes beat document-level sheets, and document-level sheets beat external style sheets.
Furthermore, properties from enclosing elements can be inherited by the
elements inside them. Notice how, in one of the examples,
the <em>
element inside
the <li>
element inherits its properties.
The process of resolving all the conflicting CSS rules is called the cascade. In practice, the rules are pretty intuitive: the rule that is closer and more specific wins.
One of the most common rules of thumb for creating clear and beautiful documents is consistency. For example, section headers should always be the same size, weight and font.
You can best achieve consistency by specifying the style far enough from the text so that the style is specified in just one place. If you have a single web page that you would like to be consistent, you should use a document-level style sheet. If you have several web pages (a web site) that you want to be consistent, you should definitely use an external style sheet. Even if your website is only a single web page but you think you might someday have a second web page using the same style, you should use an external style sheet. In short, always use an external style sheet.
Imagine that you are creating a website
for a client. For that website, you should define an external style
sheet. Then, if you or your client decides that <h1>
should be centered, <h2>
should be bold and blue,
<h3>
should be underlined and non-bold, and <em>
should be pink, you can do that in one place. If you later change
your mind and decide that <em>
should be purple, you make
one change in one file and all of your website is consistently and
instantly changed. If you had used the local style technique, you
would have to edit every page of your site, searching for and
modifying each occurrence. This is tedious and error prone.
One of the great advantages of external style sheets is also, for a web developer, a slight bother. To understand that, you have to understand caches.
The word “cache” (pronounced “cash”) is an ordinary, but uncommon, English word (more of an SAT word for most people). However, it's used all the time by computer scientists because caches are used all the time by computers, in all kinds of ways, because caching is a general technique for speeding things up.
In particular, your web browser will cache (keep a copy of) an external style sheet in a folder on your local machine (that folder is called, of course, the cache). If the web browser needs that style file again, say on another page of your site that uses the same external style file, the browser doesn't have to re-download the file; it just grabs a copy from the cache. This makes the web browser faster.
So, why is the browser cache a problem for a web designer? Because if you make a change to the external style sheet, the web browser may continue to use the old cached copy, instead of getting the new improved copy from the server. This means that when you view your page, you won't see your changes — very frustrating.
The solution is to tell the web browser to ignore the cache when you re-load the page. In most web browsers, this is done by holding down the shift key when you click on the reload icon.
So, just remember:
When in doubt, use shift+reload
The idea of CSS illustrates an important concept in the field of
computer science, namely abstraction. Abstraction is the
idea of saying what you want done without saying how to do it. By
leaving out the details about how to do something, you allow for some
flexibility, so that you can revise your method of doing the task
without messing up anything that depends on that task being
done. Here, we said <em>
without saying what that meant,
putting elsewhere the specification of how to emphasize.
Since
<em>
stands for emphasis, what could be better emphasis
than putting something in red? Unfortunately, using color to convey meaning is, in fact, a
terrible idea, because some people are color-blind or even
completely blind. We'll talk more about the issue of accessibility as the need arises
throughout this course. You should always think about
accessibility when you are designing a web page, because you want your
page to be readable by anyone, not only by people with the same
physical abilities, browser software, computer display, and network
bandwidth that you have. Some websites are legally required to be
accessible. However, for the purposes of this lecture's examples,
let's suppose that using red is a sensible thing to do.
One of the most important aspect of a page that can be controlled with CSS
is the font.
A font is a
collection of pictures of letters. A letter
is some
kind of Platonic ideal, like perfect circles or archetypal chairs. It's
also a unit of information. Any particular letter is drawn by
some person. Some are simple block letters, others are works of art
(like medieval illuminated manuscripts). A font is a
collection of these pictures, one for each character in the character set.
Fonts are studied in the field of typography, and their study is outside the scope of this course. If you are interested in their history and technique you should take the ART 222: Introductory Print Methods course offered by the Book Arts program.
For many years, fonts in web pages were restricted by the fonts that came already pre-installed in a computer. This didn't allow for a lot of variety in styling. This changed with CSS3 and the introduction of web fonts, which don't need to be installed in one's computer. Furthermore, with the use of the Google Fonts API it is very easy to incorporate such fonts in a website, as we will demonstrate in these notes.
Read the suggested chapter 8 in Head First HTML and CSS for a more detailed account on styling fonts. In the following, we give a very concise summary.
There are systematic ways in which fonts can be characterized:
You can also use abstract relative
sizes named larger
and
smaller,
that are relative to the surrounding font.
In the following we show two examples of using different font families for
the elements <p>
and <em>
. Notice
how the text changes when the second rule is added.
The easiest way to incorporate fancy fonts in your pages is to use the free service provided by Google Fonts API. API stands for Application Programming Interface. With this method, you only need to perform a few simple steps:
Add to Collection.
Useand verify your choices.
<link>
code from Step 3to your HTML file (it goes in the
<head>
section).Step 4to the CSS rules you want.
You're done. Google takes care of providing all needed format files, you don't have to provide such formats as part of your website.
Here is an example of the code you need to write to create the shadowed header below:
The <link>
line goes into the HTML file:
<head> ... <link href='http://fonts.googleapis.com/css?family=Wallpoet' rel='stylesheet' > </head>
The font-family
style goes in the CSS file (inside the rule for the desired element):
h2 { font-family: 'Wallpoet', cursive; font-size: 200%; color: black; text-shadow: 4px 4px 4px gray; }
The font was created by Lars Berggren.
The previous code works fine, but if you have a dozen pages in your
site, and you want to use Wallpoet in all of them, every one of those
pages would have to have the link
tag in the
head
. That is a lot of redundancy and is contrary to our goal
of stating the CSS information once in the shared external CSS file.
What we can do instead is, in the Google Fonts browser, instead
of using the default link
code, switch to the @import tab.
In that tab, you'll find code like this (it's the same URL as in the link):
@import url(https://fonts.googleapis.com/css?family=Wallpoet);
Copy/paste that into your shared CSS file, and you'll be able to use Wallpoet in any file that uses that CSS file.
When you use several Google Fonts for a page, the URL generated by
Google contains the operator |
to separate the different
fonts. HTML doesn't like such special characters, thus, your page will not
deemed valid by the W3 validator. To fix this issue, you should replace
the character |
with %7C
.
Beyond here is optional reading for the interested student. The material is well within your abilities, and you're encouraged to read it, but it is not required.
As we mentioned, with traditional fonts that come pre-installed in our machines, we have only a limited number of choices (since the fonts need to be installed in the machine of the viewers of the page too, not only your own). However, with web fonts (supported by all modern browsers) there are new ways to deliver fonts (either by having your files as part of your website, or by linking to other web servers) so that everyone can see the pages with the font we intended.
Be aware that not all fonts on the Web are free. For some of them you need to pay. Thus, it is better to start your search with one of the services that aggregate fonts that are free for use. Such a website is Font Squirrel, from which you can download the fonts you like. Suppose after browsing the available fonts, you decided to use the handdrawn font Desyrel.
Clicking on the Download TTF
button, you can get the file with the font (after unzipping),
desyrel.ttf
. Then, you can create a subfolder fonts
in the folder
where your HTML and CSS files are stored and then use this font as shown below:
@font-face { font-family: desyrel; src: url("fonts-files/desyrel.ttf") format("truetype"); font-weight: normal; font-style: normal; } p { font-family: desyrel, cursive; font-size: xx-large; }
This sentence was written with the Desyrel font, downloaded from FontSquirrel.
Unfortunately, things are not so simple. Because there are so many browsers and platforms, to make
sure that all of them will display the desired font, we will need to provide the font in many
different formats (Chapter 8 in your book explains these formats). Thus, the rule for
@font-face
will look a bit more complicated. The good news is that this
code and all the files can be automatically created for you by
Font Squirrel, using its menu for
WebFont Generator, after you upload the file desyrel.tff
(or some other font).
Here is how the automatically generated CSS will look like:
@font-face { font-family: 'desyrelregular'; src: url('desyrel-webfont.eot'); src: url('desyrel-webfont.eot?#iefix') format('embedded-opentype'), url('desyrel-webfont.woff') format('woff'), url('desyrel-webfont.ttf') format('truetype'), url('desyrel-webfont.svg#desyrelregular') format('svg'); font-weight: normal; font-style: normal; }
Notice that the files are referred directly, and you might need to add the relative path to the folder you'll be storing these files.
If this looks like a lot of work, there is an easier way, that doesn't involve having to keep the font files on our own server, we explain this method in the next section.
We showed here three different ways of styling fonts and that can be confusing at first. Here are some tips for you to decide what to use:
Keep in mind that your choice must be informed by the needs of the website you're building. Is the font important to convey something particular about the nature of the website? If the answer is yes, then you should make all the efforts to learn the advanced techniques we described.
When we introduced CSS rules, we described the generic form of a rule as in Figure 1. At the head of the rule is the selector, followed by a series of property and values pairs enclosed in curly braces.
Today's focus is on the selector
. The selector
indicates which element(s) the rule will apply to. So far, we
have only seen a tag as the selector, so a rule would
style all paragraphs (P elements), or all EM elements, or
whatever elements correspond to that tag. In this reading, we
will expand the set of selectors.
Back in the reading on URLs, we
learned about giving an element an ID (short
for identifier, like a driver's license is a kind
of ID card
). Because an ID is unique on a web
page, it can be the target of a hyperlink. For example, the
title of this section (see the H2 element just before this
paragraph) has an ID of css_and_id
, and so the
relative URL for this section is
http://css-selectors.html#css_and_id
.
(Try it!).
In addition, an ID can be used as the selector in a CSS rule. Thus, the following rule was used to make that H2 section header teal:
#css_and_id { color: teal; }
Notice the syntax here: the ID is preceded by
a #
symbol (pronounced hash,
or sharp
sign,
or pound sign
, depending on your awareness of
twitter, your musical training and your age).
The ID selector picks out the element with that ID,
and only that element, to apply the style to. But
what about the rule that sets all the H2 elements to light
blue, namely this following? (Later, we'll learn about
the #2EA9D3
code, but for now, trust us that it
means light blue)
h2 { color: #2EA9D3; }
The conflict is resolved by using the more specific rule, which is the one with the ID. You're probably not surprised at that.
One drawback of using an ID is that an ID is unique, so you
can only style one element using an ID. Suppose we have various
elements on our page that are important, and we want to style
them differently from non-important elements. We could number
them important1
, important2
, and so
on, but that would be unbearably tedious. Instead, we want to
describe a kind or category of element, which CSS calls
a class of element.
An example will help. Suppose we have a grocery list. We're
actually out of some critical items, so those have been given
the class important
:
Here's the source code:
<ul> <li class="important">milk <li class="important">bread <li>apples <li>bananas <li class="important">toilet paper <li>soap </ul>
You can see that class works just like ID, in that you specify
it as the value of an HTML attribute. Note that, just like ID,
you as the author of the web page get to decide what the
ID or class is. If you want to label them critical
,
or crit
or whatever, that's your choice.
Now let's turn to the CSS side of things. The important items in the list above got styled using bold red using a CSS rule like this:
.important { color: red; font-weight: bold; }
First, note the syntax of the selector. The name of the class
is preceded by a dot or a period (a full stop
if you're
British). Otherwise, it works just like an ID: all the elements
with that class get that style rule applied.
In this discussion of ID and class, we can see the concepts of abstraction and modularity that we will return to many times in this course. First of all, the ID and class allows us to separate the structure and semantics of the page (which belong to HTML) from the presentation or style of the page (which belong to CSS). Separating different aspects is part of modularity. The name that we choose for the ID or class provides the semantic label, and the CSS rule then applies some style to those labelled element(s).
A related aspect of this modularity is how to choose the
names. Suppose that instead of choosing important
as the class name, we had chosen bold_red
. It's
about the same amount of typing, and it's much clearer as to
what the class name does, right? However, trouble arises when
we change the site. Suppose have a big website, with hundreds
of pages and thousands of elements
labeled important
. Cleverly, you have one external
CSS file that every page uses, so the CSS rule is just in one
place, like this:
.boldred { color: red; font-weight: bold; }
One day, the boss comes in and
says I'm tired of the bold red; it's too scary. Change them
all to medium maroon.
You change the rule as follows:
.boldred { color: maroon; font-weight: medium; }and you're done in five minutes, with all the hundreds of pages instantly getting the new rule. The boss thinks you're a wizard. This shows the power of modularity — in this case, specifying something in one place instead of copy/pasting.
However, you now have a website
where class="boldred"
changes the contents to
medium maroon. At best, that's confusing and misleading.
The better approach is to name things by what they mean rather than by what they do. That's the power of abstraction. It's not always possible to come up with a good name, but it's worth trying to. You can read more about good class names.
Students often wonder about what the difference is between a class and an id, and when to use each one. The key is uniqueness: an id must be unique, while a class describes a kind/type/category of element.
For example, there might be many occurrences of these classes of elements:
On the other hand, there is probably only one of the following elements per page, so these might have an id.
With ID and class as tools, we can create as
many hooks
for styling as we want. In principle, every
single element could have its own ID, and we could give it a
custom style. However, that would be tedious and
confusing. There's a better way, which we turn to now.
A descendant selector creates a style rule where the
elements are chosen based on where they are in the hierarchy of
the page. Remember that an HTML document can be modeled as a
tree. At the root is the <html>
element, with
all other elements as descendants, grouped in different
families. Chrome's
Inspect Element gives a very good depiction of the tree
corresponding to the document.
Let's start with a motivating example. By default, hyperlinks are blue and underlined. That might be what you want for most of your page, but in the nav bar you want them to be, say, green and not underlined. Here's one way:
<nav> <ul> <li><a class="navlink" href="home.html">Home</a> <li><a class="navlink" href="about.html">About Us</a> <li><a class="navlink" href="contact.html">Contact Info</a> </ul> </nav>
You can combine that with the following CSS rule:
.navlink { text-decoration: none; color: green; background-color: gray; }
That's not too bad, but supposing that you also want to
adjust other elements inside the NAV (say removing the bullets
from the UL), you'd have to add more IDs and classes. You can
see that this is tedious and annoying. You really want to
say stuff inside the NAV is styled differently.
For
that, we can use
descendant selectors, meaning that all descendants of
an element are selected. (The UL is a child of the NAV, the
LI elements are grandchildren, and the A elements are
great-grandchildren.) Here's an example:
nav ul { list-style-type: none; } nav a { text-decoration: none; color: green; background-color: gray; }
With a descendant selector, you give two
selectors. The first is the ancestor and the second is the
descendant. If you have A B
, it means all
elements that match B
that are also descendants
of elements that match A
. (This can be
generalized to three or more selectors, but that's uncommon.)
In the example above, we used two tags as the
selectors, NAV UL
and NAV A
, but the
selectors don't have to be tags. Either selector could be any
one of the three basic selectors we know: (1) tags, (2) ID,
and (3) class. For example:
#banner H1 { font-size: xx-large; /* banner header is huge */ } #copyright em { font-style: normal; /* italic looks weird in tiny font */ } .appendix h2 { color: blue; /* make headers in appendices look different */ } .appendix .important { color: black; /* since it's in an appendix, don't use red */ }
In practice, you wouldn't use an ID as the second selector, since an ID is already unique and doesn't gain any specificity by adding an ancestor.
To use descendant selectors effectively, it's important to keep the tree in your mind. Here is a graphic of a tree from an example you've seen before:
Since the ancestry of an element is important with descendant
selectors, debugging can require being able to easily see the
ancestry of a particular element. Figure 3 shows the path of
elements in which our a
element is nested, as
displayed by Chrome's Inspect Element window. This is an
absolute path, starting from the root, html
. When
we specify the selector, we can use only parts of such a path,
such as nav ul li a
, or simply nav
a
.
There is one more kind of selector that will be important in this class, namely pseudo-selectors. We will look at how to format hyperlinks. As you know, hyperlinks are blue by default, but only when they haven't been visited. Once you've visited them, they turn purple (by default). You can control the style of both kind of link using CSS. You can also control what they look like when you hover over them, and in the moment of clicking on them. There are names for these four states:
a:link
is an unvisited link
a:visited
is, unsurprisingly, a visited link
a:hover
is when the mouse is over the link
a:active
is the moment when the link is clicked
Here's an example of some CSS to style all four states:
#hyperlink-example a:link { color: orange; } #hyperlink-example a:visited { color: red; } #hyperlink-example a:hover { border: 1px solid blue; font-size: 2em; } #hyperlink-example a:active { border: 2px solid red; font-size: 2em; font-weight: bold; }
Here's the example in action:
Note that there are ordering constraints among these
pseudo-selector rules; we recommend specifying them in the
order used above. You don't have to specify all of them
(:active
, for example, is pretty rare, and with
touch screen devices, :hover
is useless).
You can learn more and play with the specifications using this page: W3 Schools CSS Links.
Note that if you don't care about the state of the link,
you can specify the appearance just using the A
tag:
#hyperlink-stateless a { color: green; }
Here's the stateless example in action:
Let's summarize the kinds of selectors that we've learned:
p
, em
, li
, etc. Such
a selector will apply to all elements of this kind in the document..important
.
Such a selector will apply to all elements that share the attribute
class=classname
.h2.important
. Such a selector will apply
to the indicated elements with the
attribute class=classname
. We didn't learn
this explicitly, but it's a straightforward variation on
the classname idea.#main
, etc. Such a selector applies to the
single element that has the attribute id=idName
.nav
a
. This applies to all descendants of the selected
kind that have the selected ancestor.
To better understand the different kinds of CSS selectors, refer to the code examples in W3Schools CSS Selectors' page.
There are many more esoteric selectors, and you're welcome to learn them. The following material is optional, and you can stop here. Read on if you're curious.
If you have a rule that you want to apply to several things, rather
than copy/paste the rule (copy/paste is always bad for
modularity), you can share the rule. Suppose you have three elements on
the page with IDs of harry
, ron
,
and hermione
. If they all have different colors, but the
same font, you could do:
#harry { color: red; } #ron { color: green; } #hermione { color: blue; } #harry, #ron, #hermione { font-family: impact; }
You can see that there's just one rule to specify the font family for all three. All you have to do is list all the selectors, separated by commas. The selectors can be tags, classes, ids, even descendant selectors, in any order.
Pseudo-selectors come in two flavors: pseudo-classes and pseudo-elements.
:hover
, :visited
,
:active
, about form inputs states: :checked
,
:disabled
. Examples of using the type of an element are structural
pseudo-classes such as
:first-child
[see example],
:nth-child(odd)
[see example],
:nth-of-type(n)
[see example]. Finally, in CSS3, there is a new pseudo-class known as :not
,
which negates a rule for the selected element (that is, the rule will apply to
all other elements that are not selected). See example.
:first-line
[see example]
or :first-letter
[see example].In CSS3, to differentitate between pseudo-classes and pseudo-elements, the syntax
to refer to them has changed. An element is referred with a double colon. You will see
both examples on the Web, because they are still supported by browsers. Two new
pseudo-elements that are also interesting are ::before
and
::after
which can be used to add content before or after an element. These
are used when one wants to add icons in front or after certain elements, see
example.
The default browser we will use in this course is Google Chrome. This means that all your assignments should first work well on Chrome and only if you desire, you can make changes to make things look the same on the other browsers. For your project, once you have everything working on Chrome, you might consider adjusting the CSS rules to fix problems on the other browsers such as Firefox, Safari and Internet Explorer.
The reason for choosing Chrome is that it runs on both
MacIntosh and Windows computers, so it is neutral
between
those two operating systems. Furthermore, it is the most
popular browser on the web, according to W3School's
browser use statistics. Statistics from the
Wikipedia article
are a bit different from those of W3Schools, but both agree on the dominance of Chrome.
All browsers have tools to help developers understand how their HTML, CSS, and Javascript code is being interpreted by the browser. In these notes you will become familiar with Chrome's developer tools, most importantly the Inspect Element window. In the future, we will also talk about how to use the Console to debug Javascript applications.
You can access the Elements window in several ways; choose one that you are comfortable with:
Note that that's the letter I,
as
in Inspector
.
A new window will open at the bottom of your current web page. This window is usually docked in this position, but you can use the docking icon to dock/undock it from the main window. Figure 1 shows the complete screenshot of a web page and the developer tools at the bottom.
The first thing to notice in the Elements window is that it is divided in two panes. On the left side we see a tree representation of the nested structure of the HTML document (similar to that of folders and files in programs such as Finder (Mac) or Windows Explorer). On the right side there is a series of tabs, opened on Styles, showing what styles are applied to the selected element and how the browser has calculated the box that contains the element.
By selecting an element in the tree representation and hovering the mouse over it,
one can see how the corresponding area in the Web page is highlighted in blue,
also showing a little yellow box with the width x height dimensions of the element's content box.
In Figure 1, we notice in the upper part that the whole document is highlighted, because
we have selected the <html>
element (in the Elements window).
Spend a few minutes navigating the document tree and highlighting different tags, to see the corresponding box on the Web page.
The Chrome Inspector is a very useful tool, but it takes a little practice to be comfortable with it. Please spend a few minutes on the following exploration, because it'll help get you started using it. We'll do more practice in class and in lab.
wellesley-no-css.html
.
<ul>
is nested in <nav>
, <li>
's are nested
in <ul>
, <a>
is nested in
<li>
, etc.
<a>
tag
for Hillary Clinton and then inspect what you see in the Styles pane. Because
you haven't specified your own style for the page, the listed styles are the ones
provided by the browser (the user agent). Figure 2 shows the default style for
<a>
. For the moment, don't worry about the complicated name of the selector
(a:-webkit-any-link
), simply think that it is the style for a
. Also,
notice how below the rule for a
, the rules inherited from its ancestors: li
and
ul
are shown.
You might be confused by styles such as color: -webkit-link
,
because the value is not a kind of color. The reason for this is that the browser
keeps a table where every value such as -webkit-link
is mapped
to a real value. You can see the results of this mapping in the Computed
tab,
as shown in Figure 3.
Notice how the color
property has a real value now.
Don't worry if you don't understand
the rgb(85,26,139)
value; it's a way to represent
colors using numbers, which we will discuss a bit later in the course).
For the wellesley-no-css.html
document (without CSS styling), explore the
default styles for each element and look at what properties and values these styles have.
Here are some questions to ask yourself and figure out during the exploration:
display
and what kind of values does this
property take?display
property?
(For such cases, look at the "Computed" tab as well.h1
in the document tree, then hover with the mouse
over the colored box (it has the words: margin, border, padding). What do you see in the
web page as you perform this hovering? Repeat the same for the element ul
.em
and px
,
both used in the styles? Refer to the "Computed" tab for some help.nav
and then the element footer
.
What can you say about the selector or the rule?Now that you have explored the default styles provided by the browser, you can explore what happens when you add a stylesheet. You can try wellesley-w-css.html
In a new browser window open this second HTML
file and open "Inspect Element". Try to compare how the "Styles" pane looks for elements you have styled.
For an example, see in Figure 4 the before-and-after comparison for the h1
element.
To answer the following questions, you might need to compare several elements from the unstyled and styled document.
h1
changed? Explain how this is possible.Page layout involves placing page elements (such as text and graphics) where you choose on a page. In the past, page layout was done with special HTML tags, including tables. The modern approach is to separate the page layout from the page contents by using CSS. This has the advantage that you can have different layouts for different media (screens, printouts, cellphones) as well as keeping the HTML clean and uncluttered for those users who don't use the CSS, such as a blind person who uses screen reading software.
To understand page layout, you first have to understand that, to a web browser, a web page is a series of boxes. The boxes have a width and height, and they get assembled (laid out) on the page, much the way Ben Franklin might have laid out the text and pictures in his newpaper.
Boxes come in two kinds: blocks and inline.
A block is something big like a paragraph or
a <div>
. Blocks are preceded and followed by line
breaks, so Ben Franklin or the layout engine stacks them vertically on the
page. (Normally, a block doesn't have anything to the left or right,
though we will see some exceptions later.)
An inline element (box) is something like a word, where Ben
Franklin or the layout engine fills up a line with as many as it can, then
goes on to the next line, and so forth. You may be surprised to learn
that <img>
elements are inline, so that a line is
filled with as many images as will fit before starting the next line.
(That's why centering an image is tricky; we'll look at that a bit later.)
The <div>
element is the generic block element. A
DIV can contain any other element, including other DIVs. You can nest
DIVs as deep as you want. It has no semantics, but we can style it by
using CSS (often adding an ID or a class to the element, to distinguish
it from other DIVs). Because DIV is a generic container, we will often
use it to structure our pages. It will feature in many of the examples below.
The <span>
element is the generic inline
element. As with DIV, span has no semantics, but we can style it using
CSS, and, of course, we can add ID and class if we want.
A span can contain other inline elements, including other spans, but no inline element can contain a block element. That means you can put EM inside a P, but not vice versa. The browser may let you get away with it, but the validator won't.
The contents of a box can optionally be surrounded by a border. For example, the following CSS
p { border: 2px dashed red; }
will put a 2 pixel dashed red line around the contents of each paragraph.
Padding is the distance between the contents of the box and the border. By default, it's zero, but if you'd like a little more room between your text and the border, you can increase it. Even if you don't have a border, padding can be used to increase the distance between the contents of your box and the contents of any neighboring boxes.
Margins are the distance between the borders and the
next element. However, it's not quite as simple as that, because
the margins for vertical elements
sometimes collapse.
Margin collapse means that if a box A has a bottom margin of X and is
followed by box B with a top margin of Y, the two
are max(X,Y)
apart, not X+Y
. That sounds
weird, but it's usually what you want. If you specify that both have a
margin of Z
, they will be Z
apart rather
than 2*Z
.
Here's an example that does uses all these measures:
Now is the winter of our discontent, turned glorious summer by this sun of York.
Now is the time for all good men to come to the aid of their country.
Here's the CSS. Notice we use a descendant selector, so as not to modify every paragraph on the page.
.eg_box p { border: 1px solid orange; background-color: yellow; padding: 5px 2em; margin: 10px; }
Again, we've used short-hand properties. With padding and margins, if you give one number, as we did with the margin, you get 10px on all four sides. If you give two numbers, as we did with the padding, the first one is the top and bottom, and the second is the left and right. If you give four numbers, you can specify all four sides, in the order top, right, bottom and left (clockwise from the top). So the following is equivalent:
.eg_box p { border: 1px solid orange; background-color: yellow; padding: 5px 2em 5px 2em; margin: 10px 10px 10px 10px; }
Note that margin-collapse means that the two paragraphs are 10px apart, which is reasonable and easy to specify. So margin-collapse is usually a help rather than a hindrance.
Look at this modifying boxes using Inspect Element. (Dummy text courtesy of lipsum.com.)
In the exercise, you'll notice that contents of the box, including the padding, acquire the background color, but the margins are always transparent. Thus, padding and margins, which seem to be interchangeable when there isn't a border, aren't the same if you have a background color.
You'll also notice some big differences between blocks and inline
elements. Not only are the inline elements put together on a line,
but a <span>
can be broken up if it's too wide
(certain inline elements, such as images, can't be broken up).
Also, the width and height, and top and bottom margins of inline
elements are completely ignored.
You are probably wondering how to set all these distances. There are CSS properties to control the thickness of each side of the box (margin, border, and padding) as well as the style of the border if there is a border. There are also shorthand forms that let you define things more compactly. Consult a good reference on CSS properties
Now that you've seen some CSS properties whose values are lengths, you'll want to know what values they can have. For example, we indented this paragraph by half an inch. It's the only indented paragraph in the site. We did it using the following code:
<p style="text-indent: 0.5in">Now that you've seen some CSS properties ...
You might guess that in
means inches.
To
specify lengths effectively, you need to know the units that
CSS uses. They come in several categories:
in
), centimeters
(cm
), and millimeters (mm
).
px
.
12 point font.One point is equal to 1/72 of an inch, and is specified by using
pt
.
em
and ex
. The em
measure is named for the width of the letter Min the current font, and in CSS is defined to be equal to the size of the font. The
ex
measure is named for the height of the letter xin the current font, and in CSS is usually about half the size of the font.
If you want a paragraph to be three-fourths the width of
the enclosing <div>
, you can use code like the
following:
#content { width: 800px; margin-left: 100px; } #note { width: 75%; margin-left: 25%; } <div id="content"> <p>Here's the usual contents of the page ...</p> ... <p id="note">And here is a narrow note on the right side.</p> </div>
Look at the percentage example in action.
Pictures (image elements, produced by the <img>
tag)
are inline elements. This may seem odd, since images look so
much like big box-like things, and therefore ought to be block
elements, but they are (by default), inline elements. So, the browser
treats them like big words. Here's an example:
Bonnie and Chester were enjoying a picnic on the . Suddenly the sky filled with dark . Soon, it began to . When the began, they decided to head for .
Before we get to more complex stuff, let's look at centering content, particularly images, since that's a common desire among CS110 students. Let's start, though, with centering lines of text within a block. Here's an eye chart:
All we had to do was to use the CSS property text-align:
center
. (Note that we used an inline style sheet in the
example code, just for brevity. In practice, we would probably add a
class to this div and define it in an external style sheet).
Since images are just like big letters, the same trick will work with images:
You should almost never center lines of text in a paragraph (unless you're making lots of eye charts). You get ragged edges left and right, which looks ugly. Also, depending on the font, amount of text and the width of the region, the last line may be weirdly short (and centered). Here's a Tolkien quote:
You probably don't want that single word centered on the second line. But that can happen (and all-too-often does) when you center text and don't have complete control over browser-width, font-size, and the like.
Instead, what you probably want is to have normal, left-aligned text in a box that is itself centered. Let's see how to center boxes.
To center a block, you have to give it a width that is smaller than
its parent (which might be the <body>
), and setting its
left and right margins to be auto
. Auto
means to take
the leftover space on the line and distribute it equally among the
auto
elements. If both margins are auto
, the element
is centered. Thus:
Do not meddle in the affairs of wizards, for they are subtle and quick to anger.
Here's the code that accomplishes that:
#outer_box_example { width: 80%; border: 3px solid blue; } #inner_box_example { width: 70%; border: 2px solid red; margin: 0 auto; }
This makes the browser calculate the necessary margin for centering, and works even when the widths are in percentages. When the width is a percentage, it is calculated as a percent of its parent, not the whole window. So in this case, the inner box is 70 percent of the outer box, and the outer box is 80 percent of the area where this paragraph is. You'll notice that the outer one is not centered, though.
An alternative technique for centering images is to use the same margin
trick that works for centering other block elements, such as the
paragraph above. The difference is that <p>
is
already a block element, while
<img>
is inline. However, we can change an
<img>
element to be a block by using the
display: block
style. That is, to make an image that
is centered, use a style sheet that makes the image display
as a block
, and sets the margin-left
and margin-right
to auto
.
Note that by making the <img>
be block
,
they stack vertically, even without the <br>
tags. If
we had tried to use the text-align: center
technique, the two
pictures would have appeared next to each other on the line. (Of course,
that may be what you want, in certain circumstances.)
You should consult this web reference on Centering Things in CSS.
Consider the following example of an overfull box. Look at the CSS for it. Why does the browser have a horizontal scroll bar? The box is 100% of the container, not 110%.
The solution to this mystery is that the width of a box is defined to
be the width of its contents, not the width of the box. (If you
think this is a stupid definition, you would find many who agree with you,
but we're stuck with this definition.) Thus, if you define a box to be
100%, it had better not have any margin, border, or padding, or it will
be overfull
.
One way to avoid the creation of overfull boxes is to nest two boxes, set the width of the outer one, and then use the inner one to set the margins, border and padding. By default, the dimensions of the inner box will be determined by the outer one, which is just what you want. Here is an example of a full, not overfull box created with this idea.
You have already seen the
property background-image
in a few examples. The
background-image property will cover the screen area corresponding to
that element. If the image is smaller than the element, the image will be
repeated horizontally and vertically (like tiles on a floor), unless you
say it shouldn't be repeated. This paragraph is put on a wooden
background image by using the following CSS:
#bgimage { background: url("wood-texture.jpeg"); }
The header for this section also uses a background image. We make the image not-repeat and slide the text over by using some padding (not margin), like this:
#background_images { background: url("cs110.png") no-repeat; padding-left: 35px; }
You can learn more and try it out at this page: W3 Schools background image.
CSS3 introduces a way to round the corners of your elements using CSS alone. Each corner is treated as a quarter ellipse, which is defined by a curve that is drawn between a point on the x-axis and a point on the y-axis, as shown in the left graph below. A quarter ellipse can be regular, which means the length along both axes is the same, or irregular, which means the length along each axis is different (see graph on the right).
CSS3 introduced the border-radius
property to implement
the rounded corners. Similar to the border
property that
can be specified by
-top
, -right
, -bottom
, and -left
,
we can specifiy the x and y values for each corner separately. Below is a concrete
example:
div { border-top-left-radius: 20px; border-top-right-radius: 20px; border-bottom-right-radius: 20px; border-bottom-left-radius: 20px; }
If only one value has been specified, that means that both x and y are
using the same radius. Otherwise, we will specify two values in every
line. Clearly, if the radius is the same for all corners, we can simply
write the rule as: {border-radius: 20px; }
. Additionally,
we can use the shorthand syntax to specify different values for x and y
using this syntax: border-radius: { horizontal-radius /
vertical-radius; }
, where each side of the slash can contain
between one and four values, as with the shorthand for regular curves.
We have grouped different uses of this rule in one single page of rounded corner examples. Use the "View Source" on the browser (or Inspect Element) to look at the syntax for every rule and what it achieves on the page.
You can stop here; the following material is optional. There are some fun, eye-candy effects that you can do with CSS3, but none of it is necessary for a functional, nice-looking website.
Here is the example of a page with one background image, which was generated with this CSS rule:
header { height: 640px; background: url('images/blue-sky.jpg') no-repeat; }
To make things more interesting we can add other images on top of an existing background image, again, only using CSS3.
Here is a new page created by adding the bird on the blue sky from the previous example.
This is the CSS rule we used:
header { height: 640px; background: url('images/bird.png') no-repeat, url('images/blue-sky.jpg') no-repeat; }
Notice that we use a ,
to list all images that we want to appear
in the background. The order in which the images appear in this list is
very important. The image that needs to be behind everything else should be
last in this list as well.
However, in order for the effect to look nice, the foreground images need to have transparent background themselves. Here is how our bird image looks like if you open it with a image processing tool:
The background
property has multipe subproperties that
are useful to create more interesting effects. For example, using
background-position
we can place our bird in any
location over the blue sky. Here is the
center bottom example.
We needed to add one line to our CSS rule:
header { height: 640px; background: url('images/bird.png') no-repeat, url('images/blue-sky.jpg') no-repeat; background-position: center bottom, 0% 0%; }
Because there are two images, we specify the position for both of them. This property takes several values and you can play with them in this interactive example from the W3Schools.
Additionally, by using another property, background-size
, we
can compose scenes with multiple images, as shown in this
example with many
flying birds.
header { height: 640px; background: url('images/bird.png') no-repeat 5% 105%, url('images/bird.png') no-repeat 20% 60%, url('images/bird.png') no-repeat 40% 80%, url('images/bird.png') no-repeat 60% 40%, url('images/bird.png') no-repeat 70% 45%, url('images/bird.png') no-repeat 60% 50%, url('images/blue-sky.jpg') no-repeat 50% 50%; background-size: auto, 10%, 10%, 5%, 5%, 5%, auto; }
The background-size
property can also
take values of different kinds. The values in % shown in our
example, refer to the size of the parent element of the background, in this case
<header>
. Since its size is 960x650 pixels, a 10% image will show as
96x65 pixels. Finally, notice that in this example, we have added the
background-position
values to each image (after no-repeat
).
Each of the pairs shows the left and top coordinate for where the image
is placed.
We strongly recommend that you read Chapter 11 of the Head First HTML and CSS book as a gentle explanation for this topic. The material in these notes comes from Chapters 11, 12, 13 of the book CSS The Missing Manual.
As you have noticed so far, HTML displays content from top to bottom, with most elements stacked as blocks. Today, we will see how to use CSS to put elements everywhere in the page, in order to create more visually appealing pages.
Most web page designs on the web fall in one of the two types of layout: fixed-width or liquid.
Fixed-width design gives you more control over how your pages will look, but will cause inconvenience for users with small monitor devices (a lot of horizontal scrolling, which people dislike). Liquid designs, which grow or shink to fit a browser window, are better for users, but make things more difficult for you the designer.
Web page layout involves putting content into different regions of the
page. In order to do this, the content needs to be in container tags such
as: nav
, header
, section
, footer
, picture
, aside
,
and the ubiquitous div
. Keep in mind though that you don't
need to use div
if it's not called for. Elements that are
displayed as blocks, such as ul
, ol
,
and p
can be also moved everywhere in the page.
There are two general techniques used for layout: floats and absolute positioning. Floats are by far the most used, as it allows the layout to be fluid. Also, with float layout, content never overlaps other content, so even if the layout isn't pretty, it's functional. With absolute positioning, you have the power and burden of complete control. We'll look at both in this reading.
To use float, you start with the normal layout of boxes stacked
vertically on the page, but you can allow certain elements to move to the
side and have other material flow around them. To do that, HTML elements
have a property float
, which will make the element float to
either the left or the right side of the page (your choice). Of course,
other material can't flow around it unless it is narrow enough, so we
also have to consider the width of the element. We'll look at that, too.
Absolute positioning allows you to place an element anywhere on the
page with pixel-like accuracy. To do so, CSS has a position
property that you set to absolute
, and then use additional
properties like left
and top
to specify the
location of the element. This technique doesn't go well with the fluid
design, but is good for positioning things like logos, etc. that need
always be in a certain place. Absolute positioning is sometimes used in
a limited way as part of an overall layout that mostly uses float.
Web page layout with CSS is more of an art than science; there is no formula for marking up your HTML page or creating the CSS. CSS layout is something that you will learn through experience, learning the different CSS propertis, following tutorials, and practicing a lot. What follows in this section is a set of guidelines that can be useful as you start learning about this topic.
background-image
property than the
<img>
tag. This is because you can put other
information on top of this image. However, you should know that
background images are not printed, thus, don't put important
information (such as maps) as background.background-image
property to put text on top of
images, or use the position
property to lay images or
icons on top of text. You can't use float
to make
content overlap.In this section, we will show with different examples how to build
float-based layouts. Such layouts make use of the float
property to position elements side by side and create columns. You can
also use float to create a wrapping effect. This is because the floated
element is removed from the flow of the document and what comes after it
moves up and wraps around the float. (Note: Your book
has a very detailed description of what the flow of a document is.)
As you know, an image is an inline element. To remind you of
that, look at this example and
resize the browser page to see what happens to the images. You also
can use Chrome's Inspect Element to notice
that <img>
is nested within
the p
.
Now, let us look at the same page after applying the following style rules:
margin: auto;
for it.
img { float:
right; }
.
Notice how the images are now strictly on the right side of the document, and the text of the paragraphs wrap around them. The only step that is necessary for this is the last step, but the others are nice, too.
Let us now see what happens when we float a container element, such
as a div
, figure
, footer
, etc.
Here is the unstyled page,
where we put the img
inside the figure
container. Notice how this time the images are not inline anymore.
If we apply now the floating property to the figure
,
element, we get the same effect
as before, with the distinction that
figure
already has some default values for the margin assigned
by the browser.
The float
property takes only three
values: left, right, and none. The last value
is used when you want to prevent an element from floating. (There is
no float: center
property; we discussed centering in the
reading on the box model.)
Now that we learned about the property float, let's see how it can be used to create a layout with columns.
In order to have a two-column layout, we need to have two containers
which can meaningfully stand next to each other; for example,
a section
and an aside
. Let's add first
a aside
element to our
Hunger Games example. The
content will be a header and a list with short lines:
<aside> <h3>Characters</h3> <ul> <li>Katniss Everdeen</li> <li>Peeta Mellark</li> <li>Gale Hawthorne</li> <li>Primrose Everdeen</li> <li>Haymitch Abernathy</li> </ul> </aside>
Then, we apply some minimal style to this element, in order to float on the left side, as shown below, getting this new version.
aside { float: left; border: 1px black solid; padding: 5px; }
Important Note: The width and height of every
floated container element depends on the amount of content
(text or images) inside the container. This is the shrink-to-fit
model. If the list above had had some very long items, so that the list
was as wide as the page, the float
property would have no
effect.
As you see from the screenshot below, the aside
box size
can be calculated based on the values for margin, border, padding, and
content size (e.g. the width: 185px = 0 + 1px + 5px + 173px + 5px + 1px
+ 0). The 173px comes from the how wide the text in the header and list
are. Again, if you have a big paragraph of text in your floated element,
it's likely to be as wide as its container, and the floating will have
no effect.
If you want the size of the column to be something you desire (e.g.,
instead of width of 185px, to be 200px), you can do the reverse
calculation to find what the width of the content should be, using a
simple equation. For example, 200px = 0 + 2*1px + 2*5px
+ Xpx. Solving this, we
get width=188px
. This means that in our rule we should set
the width: 188px;
, in order to get the real size of the
column to 200px on the browser page.
One could do the same for the height, but this is not very useful, because if the adjacent column gets more content, we'll need to recalculate.
Now, to achieve our two-column effect, we simply add a margin to the
non-floating element. Here, we add a margin to the section so that the
aside floats next to it. Depending on whether the floating column would
be on the left or the right side, we need to
set margin-left
or margin-right
to a value
slightly larger than the width of the floating column. Since the aside
is 185px wide, a margin of 210px should look nice. Therefore, we add the
following rule:
section { margin-left: 210px; }
This is how the finished two-column layout looks: our two-column example.
aside
and section
(or a div
with an id
attribute).aside
) either left or right.Going from a two-column layout to a three-column layout is a fairly straightforward extension. Can you think of the changes you will need to make to your HTML and CSS code? To challenge yourself, stop reading at this point and go and experiment with the code from the two-column layout (get the files from the links above). Then, check our solution. Here is a summary of the steps we performed:
aside
element (before the section
).aside
element.float
.margin-right: 210px;
to the section rule, in order
to allow space for the new floating element.Important Note: When using this technique (known as positive margins), the floating elements need to be placed before the main content in the HTML file, otherwise, you will not get the desired effect.
Working with floating elements can often cause unexpected problems. In the following, we will describe two of them together with the solutions.
We saw that one of the things that happens with floats is that other
content wraps around them. Often that's what we want, but sometimes it
isn't. A problem that occurs often is with the footer
element of a page, when the main content is shorter than one of the
floating columns. To illustrate this problem, we modified the
Hunger Games example
to show what happens with the footer. Here is a screen shot:
Fortunately, there is an easy fix for this problem. We add the property
clear
in the rule of the element we want to stay away from
the floats, in our case, footer
. What clear
means is to move the cleared element down, below any floating
elements. Like this:
footer { clear: both; }
After making this change, the footer stays at the bottom of the page.
Another problem occurs when a floating element contained within another
element is larger than its container. This becomes obvious when the
container has a background color or border. In our
ongoing Hunger
Games example, we changed the background color of
the section
element to olive, so that now you can see, in
the screenshot below, that the floating element (the figure) had come out of
its container (the section). It had always been outside its container,
but we just hadn't noticed.
There are several solutions to this problem, but we will show the two simplest.
<br class="clear">
element before the end of the section and a CSS rule for
it br.clear {clear: both;}
.overflow: hidden;
. Here is
again solution
using overflow hidden. While this CSS property works very well,
its name can be confusing, since the effect is not to hide the
overflow (the floated element that sticks out), but instead it
enlarges the containing element so that the floated element no
longer sticks out.
At the beginning of these notes we mentioned that floats are the most used technique for designing layouts. However, there is another technique, positioning, which has its good uses in particular situations.
We achieve positioning of elements through the
property position
, which can take these values:
The values absolute and fixed are very similar in their syntax (though they create different effects) and more easy to understand. The value relative is a bit more tricky, because its meaning is not in par with how we use the word in everyday language. To explain these values, we will show in the following an example for each. You should look at the HTML and CSS files of each example to better understand what is going on.
We can use this style to set an element in a desired location in the
page, by additionally specifying a horizontal and vertical position
using the
properties left
, right
, top
,
and bottom
. These properties specify the distance (in
pixels or some other units) from the
(0,0) coordinates of some reference container. Usually, the reference
container is the viewport (top-left corner of the browser
viewing area).
To see this in action we have
a modified
version of our Hunger Games example, and we will try to position
the figure
to some other location, as shown in
this styled
version of the example.
This effect was achieved with this CSS code:
figure { position: absolute; top: 350px; left: 500px; border: 1px black solid; padding: 3px; }
Notice how the figure lays on top of the text. This is because by becoming absolute, the other elements are not aware of it anymore, so they cannot flow around it. Additionally, try to resize your browser window. The placement of the figure doesn't change.
Suppose we want to put a caption on the figure, sitting on top of it, instead of below it. In this case, we will need to use the relative position. However to achieve this, we need to do two things:
This new example, creates a caption over the figure with the following code:
figure { position: relative; border: 1px black solid; padding: 3px; width: 400px; }
figcaption { position: absolute; bottom: 15px; left: 4px; right: 4px; color: yellow; background-color: black; opacity: 0.5; text-align: center; font-weight: 200%; padding: 5px; }
So, the bottom
and left
lengths in
the figcaption
are measured from the upper left of
the figure
, not from the window.
This positioning is very similar to the absolute one, with
the difference that the element remains in its position all the time,
while the rest of the page scrolls up and down. You can think of it as
positioning relative to the screen. It is useful for fixing
navbars or sidebars in one position. See how the aside
element remains fixed
in our
example, with the code below:
aside { position: fixed; top: 80px; border: 1px black solid; padding: 5px; width: 188px; /* this value will make sure the sidebar box will occupy 200px */ }
A computer is a complex electronic machine, but its operations can be understood sufficiently in terms of a few interconnected components:
The processor is further divided into two smaller components
In the picture note that the components are connected through a set of wires called the bus.
The operations of any processor are as follows ad infinitum ("forever"):
This is called the fetch/execute cycle. Note that the processor is so much faster than the I/O devices, that I/O doesn't appear prominently here. A modern processor is able to execute millions of instructions while waiting for an I/O device, even a fast device like the disk or the network.
The main point here is that the computer doesn't know how to do anything "automatically": there's always some program code (set of instructions) telling it how to do something.
The input/output unit is connected with the usual peripherals such as keyboard, mouse, the various "drives" (such as hard drives, floppy drives, zip drives, DVD drives, CDROM drives, etc), monitors, printers etc. So, the components and operations of a computer are remarkably simple. Complication enters only for performance reasons, but any computer that you are likely to see these days contains the above components.
Looking at the picture above, you see a big yellow box
labeled Memory
, but you'll also see, if you look closely, hard
drive
attached to the bus. What's the difference? Does it matter?
Yes, it does. The memory we describe there is super-fast memory chips that are right on the circuit board, just a few inches from the processor, what is sometimes called RAM or Random Access Memory. The most important properties of RAM are
If you've ever lost a document you were working on using your computer because the power went out or your battery ran out, that's because the stuff you were working on was in volatile memory. The files that are saved on the hard drive weren't lost.
The hard drive of your computer is (usually) a spinning disk with magnetic coating that stores the zeros and ones of your files as a pattern of magnetization. The exact representation isn't important, but two properties of hard drives are crucial:
There are other kinds of drives, such as solid-state drives, but the same properties appear. These properties mean that your computer mostly works by copying information (both programs and data, such as your files) from long-term storage on the hard drive into fast memory in the RAM, and that's where it stays while the computer is running. But you should save your stuff in RAM to hard disk on occasion, in order to avoid losing your work should someone trip on your power cord and pull the plug, or your battery unexpectedly die.
Finally, the most recent technology (invented in the past 15 years) is the USB flash drive , which has become the ubiquitous choice for transferring information between devices.
Every description of a computer needs to explain how the computer handles information: numbers, text, pictures, sound, movies, instructions.
The computer is an electronic device. Each of its wires can either carry electric current or... not carry current. So, like a light switch, it understands only two states. It turns out that this is enough to make the whole idea work. In fact, any system that can represent at least two states can represent information. Take, for example, the Morse code that is used in telegraphy. Morse is a sound transmission system that can carry a short beep (represented by a dot) and a long beeeeeep (represented by a dash). Any letter or number can be represented by a combination of these two symbols. Click here to see a Morse translator.
Similarly with computers. To represent a number, we use the binary arithmetic system, not the decimal number system that we use in everyday life. In the binary system, any number can be represented using only two symbols, 0 and 1. (Morse is almost, but not quite (due to the pauses between letters) a binary system. A system closely related to Morse is used by computers to do data compression (more about this later). Here is how the binary numbers correspond to our decimal numbers:
Decimal | Binary |
---|---|
0 | 0 |
1 | 1 |
2 | 10 |
3 | 11 |
4 | 100 |
5 | 101 |
6 | 110 |
7 | 111 |
8 | 1000 |
9 | 1001 |
10 | 1010 |
11 | 1011 |
12 | 1100 |
13 | 1101 |
14 | 1110 |
15 | 1111 |
And so on. Both systems are positional: a great idea that we owe to Arab mathematicians, because before them, counting in Roman was tough (DCCCLXXXII + CXVIII = M, you know...) and counting in Greek was almost impossible (omega pi beta + rho iota eta = alpha).
Positional means that the position of each symbol within the number determines its value. Thus, 32 is different from 23 because the 3 and the 2 are in different positions, and each position has a place value. You already know this, but you do it instinctively, and we are reminding you of this because we will do the same thing with binary numbrs.
For example, you know that the meaning of the number 1492 is:
1492 = 1*1000 + 4*100 + 9*10 + 2*1 = 1*103 + 4*102 + 9*101 + 2*10
Thus the meaning of every digit is defined by the power of 10 in that position.
Similarly, number 1101 in binary means 13 because
13 = 1*8 + 1*4 + 0*2 + 1*1 = 1*23 + 1*22 + 0*21 + 1*20
The numbers 8, 4, 2, 1 are, as you know, powers of 2, starting on the
right and moving left: 1 = 20
,
2 = 21
,
4 = 21
, and 8 = 23
The decimal system is also called "base 10" and the binary "base 2", because every digit in a number contributes to the whole based on the power of 10 (or power of 2) with which it is multiplied.
Of course, we can have positional systems on different bases, like base 12 (AKA "a dozen") and base 7 (AKA a week). Very soon in this course, we'll work with base sixteen.
Below is a form that can help you convert between bytes (8-bit binary
numbers) and decimal numbers easily. Type in a decimal number and
press enter
or click elsewhere on the page to update the
checkboxes. Or, click the checkboxes and watch the decimal number
change. A checked box corresponds to a bit that is one. An
unchecked box corresponds to a
zero. Try creating the number 13 by clicking on the 8, 4, and 1
checkboxes. Play around with this to get comfortable with it.
What's so grand about a positional system? Arithmetic calculations are much easier than in non-positional systems, Can you imagine what second grade would be like if you had to calculate that XLVIII + LXVII = CXV?
Here are some tutorial videos on binary numbers:
One of the key themes of this course is about representations. Computers represent lots of interesting things, such as colors, pictures, music, videos, as well as mundane things like numbers and text, or even complex things like programs. Ultimately, you know that, at the lowest level of all those representations, there are bits.
One of the important aspects of these binary representations is the relationship between the number of bits and the power of the representation. The more bits we have, the more stuff we can represent.
Let's be concrete for just a minute. You saw above that with one bit,
you can number two things: the one labeled "zero" and the one labeled
"one," because there are two possible patterns: {0, 1}. With two bits,
you can number four things, because there are four possible patterns: {
00, 01, 10, 11 }. With three bits, you can number eight
things. (Can you list the eight patterns? Take the four patterns of two
bits and write them twice, once as 0xy
and again
as 1xy
.) Thus, whenever we use an additional bit, we get
twice as many patterns.
Here's a table of the relationship between the number of bits and the number of patterns:
Number of Bits | Number of Patterns |
---|---|
1 | 2 |
2 | 4 |
3 | 8 |
4 | 16 |
5 | 32 |
6 | 64 |
7 | 128 |
8 | 256 |
... | |
16 | ≈ 65,000 |
... | |
24 | ≈ 16 million |
... | |
32 | ≈ 4 billion |
... | |
N | 2N |
This fundamental relationship is important and is also unintuitive, because it is exponential. The last line of the table shows us the N bits yields 2N patterns. This is unintuitive because, if you double the number of bits, you don't get double the number of patterns, you get the square. For example, double 3 bits (8 patterns) to 6 bits, and you get 64 patterns.
We'll see this relationship come up in, for example, our discussion of indexed color, because the limits we place on the number of bits in the representation results in a limit on the number of colors. This is a key idea.
Text is represented with the so-called ASCII code. Years ago, the manufacturers of early computers decided to represent every possible character (visible or invisible, like the space or the newline) with a number. The result was (partially) the code you see below.
A more comprehensive ASCII table is the following, which includes control characters with their associated graphic symbols, and, for each character, its hexadecimal, decimal and binary codes:
For example the letter E has the ASCII code of 69 and a binary representation of 01000100. The 8 bits are depicted in groups of four, because four bits are used to represent a single digit in the hexadecimal system, that we will discuss later.
The first two rows of the table represent so-called control characters, characters that are not visible, such as backspace (BS), escape (ESC), CR (carriage return - an old word for enter), etc. If you are interested in all acronyms, the AsciiTable website explains them in detail.
You'll notice that the first table above starts with ASCII code 32, which is for the space character. (Yes, even the space character needs to be represented.) The actual ASCII system starts at 0, but the first 32 characters are "control" characters, because they were originally used to control the early printers. For example, the TAB character is ASCII code 9, the line feed character (which moved the paper up) is ASCII code 10, and the carriage return (which moved the print head back to the left edge of the paper) is ASCII code 13. Since those characters are not interesting in the context of this class, we've omitted them from the table.
If all we had to worry about was characters, text representation would be pretty straightforward. However, text is organized into lines, and for historical reasons, one of the subtle differences among Windows, Mac and Unix/Linux is how line endings are represented. In the olden days before Windows, Macs and Unix, the early teletype printers used two control characters at the end of each line: the carriage return character to move the print head back to the left, and the linefeed character to move the paper up by one line.
The Mac represents the end of a line with a carriage return character. Linux uses a line feed character. Windows uses both, just like the olden days.
Usually, when you transfer a text file from system to system, the FTP program (Fetch, WinSCP, or whatever) substitutes the appropriate line ending. The "text mode" of transfer says to make these substitutions; "binary mode" makes no substitutions and is more suitable for non-text files, such as images or programs. Note that HTML and CSS are both kinds of text. Most FTP programs have a "guess which mode" setting that usually works pretty well, but can occasionally make a mistake, which is why it's sometimes useful to know this obscure fact. It's also why copy/pasting between Mac and Windows often fails.
The early ASCII system had space for 256 symbols, enough to represent all English characters, punctuation marks, numbers etc. It turns out that there are other languages on Earth besides English, (;-) and recent software is being written to accommodate those, too, via a much larger code called Unicode. We have already been using unicode when putting this tag in our HTML:
<meta charset="utf-8" >
Your Head First HTML & CSS book talks about unicode on page 239.
Groups of bits that are used to represent characters came to be known as a byte. Remember that a byte is 8 bits. That Wikipedia page also discusses the history of the word and the names for larger groups of bytes, such as
Bis used for bytes while lowercase
bis used for bits. Network speeds, where bits go across a wire one at a time, are usually measured in bits per second, or bps. File sizes are always in bytes, hence kB or MB.
kilois a lowercase
k, hence
kBfor kilobytes. The abbreviations for the other prefixes (
mega,
giga,
tera,
peta...) are all uppercase: M, G, T, P ....
Historically, computer scientists have often used kilobyte
to mean 1024 bytes, because 1024 is pretty close to 1000 and because
bytes often come in chunks whose size is a power of 2, and
1024=210. For example, if you buy a 4 GB flash drive, it
won't hold exactly 4 billion bytes, but a bit more because of this
difference. Hard drives and network speeds, on the other hand, are
usually measured in powers of 10 rather than powers of two. In
practice, it rarely matters.
Computers these days come with huge amounts of storage space on the hard drive (often hundreds of GB), but they are usually able to process only a few GB of them at a time (their main memory or RAM). We will use these symbols often in future lectures.
Very often in a website, there is repeated material on each page. For
example, each of the lecture pages in this course has a navbar
at the top
that
includes the links to the major components of the website.
You certainly could copy/paste the repeated items to each page, but that makes maintenance very difficult: if you decide to change the common content, you have to edit every page, and you have to make sure you don't miss any and that you edit them all in the same way. For example, adding a new section to your website that is accessible from the navbar would require changing every single page! For a small website, this is bad, but for a large website, it is completely unreasonable.
A solution is to break your page into pieces, each stored in a different file, and have the server cobble them together whenever it responds to a request for your web page. This technology is called Server Side Includes or SSI. Exactly how to use it depends on the server software. On the CS web server, we use Apache to serve web pages. For more detail, you can read the Apache tutorial on Server Side Includes.
Suppose we have three pages, A, B, and C, all of which should share a common header and footer, each of which is in a separate file. These shared pieces will be included into the main pages. Thus, we have five files, none of which is a complete page:
In the list above, there are two kinds of files:
.html
This kind of file includes
another file as part of itself. The .html
files are the container
files, so they have all the usual infrastructure: They start with
a doctype
element and have tags
like html
, head
, and so forth.
.part
This kind of file is included
in another file. It's just a fragment of a web page, such as just the
navbar or just the footer. It doesn't have
a doctype
element, thus, it is not a valid HTML document.
In the source code of the HTML files A, B, and C, we put a special marker, called a directive, that the server will look for and replace with the contents of the included files. The directive looks like an HTML comment, so that if the SSI doesn't happen for some reason, the directive won't otherwise mess up your code. Let's look at what page A's source code looks like:
<!--#include virtual="header.part" --> <p>Here is some content that is unique to <strong>page A</strong> of the website. <!--#include virtual="footer.part" -->
The text in quotes after the word virtual
is a relative URL for the
file to be included at that point in the main file.
Note that if you View Source
on ssi/A.html
, you
won't see these directives. Instead, you'll see the included content.
The server obeys the directives and constructs the page from the parts and
sends it out, and the browser has no way of knowing what the original file
was, or even that SSI was involved at all. SSI is completely invisible.
In order for the server to know that it has to do some extra work in
cases when the files contain directives,
we must make the
file executable. Files on the server can be readable,
writable, executable, and any combination of these three operations.
These are known as file permissions
and can be set through the
SFTP interface (CyberDuck, Fetch, WinSCP, FileZilla etc).
We will always have to set the execution bit on, in order for SSI directives to work. Figure 1 shows how the folder for our example looks like. All HTML files have an extra x value in their list of permissions. We will show in lab how you can do that for your files.
Note that it's the web server that does the work, so SSI will not work when viewing files on your local desktop.
Here is a copy of A.html as it appears on the server. If you view the source, you can see what the code looks like before the directives are included.
One common error is to put a space after the comment syntax:
<!-- #include ... -->
Unfortunately, this is treated as an ordinary comment, not an SSI directive, so be sure you do it exactly like this:
<!--#include virtual="relative/path/to/file.part" -->
Second, if you make a mistake in your SSI syntax or in the relative URL, you'll see something like the following in your main file, when you view it on the web:
[an error occurred while processing this directive]
For an example of this, see the file C-broken.html, in which we intentionally put in a typo, changing the line
<!--#include virtual="header.part" -->
to
<!--#include virtual="jeader.part" -->so that you can see such an error in action.
Finally, if you make a change to your .part
file, you may
not see any change when viewing the .html
file in your
browser. This is very confusing and frustrating: didn't you change that
web page?!
This happens because the server told the browser that
the .html
file hadn't changed (which is true), and so the
browser saved time by just showing you the copy in the cache, instead of
re-downloading the .html
file. On many browsers,
you can tell the browser to ignore the cache and
really reload the file by holding down the shift
key while
reloading the file (in a Mac: Shift + Command + R).
You also need to make sure that your .part
file is
accessible (readable by all) from the web.
Just as with URLs, you should use relative paths in
the virtual="path"
part of the SSI directive, so that they
will still work when you move your website to another server. (The
server should be one that supports SSI.)
However, the issue of relative paths can be a little tricky. Suppose
that the file harry.html
uses SSI to include a
file items/firebolt.part
. Furthermore,
the firebolt.part
file in the items
subdirectory has some code referring to Harry's
friend ron.html
. Is the URL for ron.html
relative to the HTML file or the PART file?
The answer is that it's relative to the HTML file. This is because the relative linking is done by the browser, which is completely unaware of the use of SSI, since all the SSI magic happens on the server, before the browser even gets to see the page. SSI is like dynamic copy/paste, so the code in a part file is just the same as it would be in the HTML file.
Overall, SSI is one of the right approaches to avoid redundancy in websites (there are others we won't cover). Done correctly, a visitor to your site will never know that you are using SSI; it's just the case that content that should be consistent from page to page (banner, nav bar, header, footer, and so forth) is indeed consistent.
However, SSI is often a pain for the people developing the website. The reason is that SSI is only done by the server. That means that if you have downloaded an HTML page from the server and are editing it locally on your desktop, and you view the local copy in the browser, all the SSI content will be missing. You won't see that banner, nav bar, and all that. If you view the source, you'll just see the comment. The layout of your page may be messed up because that content is missing.
So, what's the best thing to do? Our recommendation is the following.
(For concreteness, assume you are editing fred.html
and
that page includes nav.part
. )
fred.html
, download
both fred.html
and nav.part
.
nav.part
into the correct
place in fred.html
.
nav.part
. If you discover that that code needs
editing, you have to realize that you are essentially simultaneously
editing all the pages in your site. So, do the following:
fred.html
and test that it
works.
nav.part
file.
nav.part
file to the server,
and
nav.part
.
nav.part
file to their own working copy.
fred.html
, delete the copy of nav.part
before uploading fred.html
to the server. Otherwise,
you'll end up with two copies of the content on that page!
Yes, this is a bit tedious, but hopefully you will rarely discover that
the code from nav.part
needs to be edited, so you'll only
have the extra steps of copy/pasting and subsequently deleting.
Once you've implemented SSI, an alternative to working on your files locally is to edit your files directly on the server. This way, when you're making edits, you can view your files live on the server, and the SSI will work. No copy/pasting necessary.
To do this in CyberDuck, connect to the server and locate the file you want to edit, right click it and choose "Edit With ... Atom."
The file will appear in Atom, but you'll be editing the version on the
server. (Well, actually a local copy, but CyberDuck ensures that anytime
you save the file, the local copy is immediately copied to the server.)
You'll view the changed file using the
usual http://cs.wellesley.edu/~username/
URL, not
a file:///
URL.
Since SSI is done by the server, using it depends on the server software. SSI is supported by Apache and lighthttpd, but not by IIS, which is Microsoft's web server software. Apache is currently the most widely used web server software, but your client's web hosting site may not use it.
Should this occur, you could try to replace the SSI code with
JavaScript, but that may not be accessible for all users (for example,
screen readers may disable JavaScript code). An alternative is to
simply copy/paste the .part
code in
the .shtml
, but that loses all of the advantages of using
SSI. A last alternative is to use a more powerful and portable
server-side scripting language, such as PHP, but that's outside the
scope of this course.
To do SSI, you must:
.partfile (the one you'll be including in other files)
.htmlfiles
.htmlfile. You may need to use
shift+reloadin your browser to ignore the cache when refreshing the page
Building upon the idea of representation, we will discuss how images are represented in digital form. We'll work up to it, first starting with how color is represented (which is based on the physiology of the human eye), then looking at images as rectangular arrangements of spots of pure color. Finally, we'll calculate the file size of an image and discuss one way of compressing the file so that it is smaller and therefore faster to download. This compression is, in fact, a different representation of the information.
In the present day, modern browsers support 140 color names. This
means that we can use color names such
as black
, aqua
, or chocolate
as
values for the CSS properties that except a color value, such
as color
, background-color
, etc. Many years
ago, browsers could only support 17 color names, known as standard
colors: aqua, black, blue, fuchsia, gray, green, lime, maroon,
navy, olive, orange, purple, red, silver, teal, white, and
yellow. However, later that list was expanded with 123 more
colors. W3Schools maintains a complete list of
the 140
recognized color names. While you can achieve a lot by using only
these named colors, very often you want something more specific from
the color spectrum. It turns out that we can use numerical codes to
refer to colors, because inside the computer, colors are represented
by numbers. How? For that, we need to understand additive colors and
color vision.
Human retinas happen to have rod-shaped cells that are sensitive to all light, and cone-shaped cells that come in three kinds: red-sensitive, green-sensitive, and blue-sensitive. Therefore, there are three (additive) primary colors: Red, Green and Blue or RGB. All visible colors are seen by exciting these three types of cells in various degrees. (For more information, consult these Wikipedia articles on additive color and color vision.)
Color monitors and TV sets use RGB to display all their colors, including yellow, chartreuse, you name it. So, every color is some amount of Red, some amount of Green, and some amount of Blue.
On computers, RGB color components are standardly defined on a scale from 0 to 255, which is 8 bits or 1 byte.
Play with the Color slider page to get a feel for this.
Here is a list of examples:
We can use this knowledge about colors being represented as a mix of red, blue, and green when specifying color values in CSS. There are three ways to do this:
color: rgb(64,224,208); /* three RGB numbers in the range 0-255 */ color: rgb(25%,88%,82%); /* three RGB percentages */ color: #40E0D0; /* three RGB numbers expressed as a hexadecimal triple */
The first two ways are self-explanatory, since they use decimal numbers
and percentage values with which you are familiar. In the following,
we will explain the meaning of the hexadecimal color codes such as
#40E0D0
. The #
sign is used in this case
to simply indicate that the sequence of digits and letters is in
hexadecimal.
People use decimal (base 10), computers use binary (base 2), but programmers often use hexadecimal (base 16) for convenience.
Binary numerals get long very fast. It is not easy to remember 24 binary digits, but you can more easily remember 6 hexadecimal digits. Each hexadecimal digit represents exactly four binary digits (bits). (This is because 24=16.)
One way to understand hexadecimal is by analogy with decimal, but we're all so familiar with decimal numerals that our reflexes get in the way. (In fact, humans throughout history have used many different numeral systems; decimal is not sacrosanct.) So, we first need to break down decimal notation so that you can see the analogy with hexadecimal. For now, we'll stick with two-digit numerals, but the same ideas extend to any larger numbers.
Decimal notation works by organizing things into groups of ten, then counting the groups and the leftovers: Suppose you had a bunch of sticks on the ground and you bundled them all into groups of 10 with some left over (fewer than 10). Now, use a symbol to denote the number of bundles and another symbol to denote the number of sticks left over. You've just invented two-digit numbers in base 10.
Hexadecimal: Do the same thing with bundles of 16, and you've invented two-digit numbers in base 16. For example, if you had thirty-five sticks , they could be bundled into two groups of sixteen and three left over, so the hexadecimal notation is 23. Careful! That numeral isn't the decimal number twenty-three! It's still thirty-five sticks, but we write it down in hexadecimal as 23.
To distinguish a decimal numeral from a hexadecimal numeral, we use subscripts. So, to say that thirty-five sticks is written 23 in hexadecimal, we can write:
3510 = 2316
Both decimal and hexadecimal notations are based on place value. We say that 2316 means 3510 because it's a "2" in the sixteens place and "3" in the ones place, just like 3510 has a "3" in the tens place and a "5" in the ones place.
Let's take another example. Suppose we have 2610 sticks. That's one group of 16 and 10 left over. How do we write that number in hexadecimal? Is it 11016? That is, a "1" in the sixteens place followed by a "10" in the ones place? No; that would be confusing, since it would look like a three-digit numeral. We need a symbol that means ten. We can't use "10," since that's not a single symbol. Instead, we use "A"; that is, A16=1010. Similarly, "B" means 11, "C" means 12, "D" means 13, "E" means 14, and "F" means 15. We don't need any more symbols, because we can't have 16 things left over, since that would make another group of 16. The following table summarizes these correspondences and what we've done so far.
Decimal | 0 | 1 | ... | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ... | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Hexadecimal | 0 | 1 | ... | 9 | A | B | C | D | E | F | 10 | 11 | 12 | ... | 1C | 1D | 1E | 1F | 20 | 21 | 22 | 23 | 24 |
To convert a big decimal number to hexadecimal, just divide. For
example, 23010 divided by 16 is 1410 with a
remainder of 610. Thus, the hexadecimal numeral is
E616. To convert a hexadecimal number to decimal, just
multiply: E616=E*16 + 6 = 14*16 + 6 = 230
.
Try the following conversions as an in-class exercise. You can use a calculator, you can ask your neighbors, anything you like.
Dec | Hex | Dec | Hex |
---|---|---|---|
7 | 22 | ||
26 | 100 | ||
127 | 149 | ||
240 | 255 |
You can check your work with the following form:
Now that we know both hexadecimal and binary, you can convert binary to hexadecimal (and vice versa). However, you would probably do so by converting the binary number to decimal and then the decimal number to hexadecimal. There's a better way, involving almost no arithmetic (or, rather, all the arithmetic is with one-digit numbers you can add in your head). Indeed, this technique is the reason that computer scientists like using hexadecimal. (Well, this and the fun of getting to spell words like ACE and DEADBEEF with hex digits.)
Let's start with an example. Suppose you need to convert the following from binary to hexadecimal:
01010100 = ??16
What we're going to do is to take the bits in chunks of four bits, so to mark the chunks we'll insert a period in the middle of the number:
0101.0100 = ??16
Now, we just convert each chunk directly into hex. The first chunk, 0101, is just the number 5. The second chunk, 0100, is just the number 4. Those are already in hex, so we are done:
0101.0100 = 5416
(Try doing it via decimal, to check. The decimal value corresponding to both of these is 80+4=84.)
Let's do another one, this time with slightly larger values:
10101100 = ??16
Again, take the bits in chunks of four bits:
1010.1100 = ??16
Now, we just convert each chunk directly into hex. The first chunk, 1010, is 8+2 or 1010, which is the digit A in hex. The second chunk, 1100, is 8+4 or 1210, which is the digit C in hex. So we are now done:
1010.1100 = AC16
(Again, check our work by doing it via decimal. The decimal value corresponding to both of these is 160+12=172.)
Why does this work? Suppose we needed to convert 172 from decimal to
hex: our first step would be to divide the number by 16. In binary,
moving the binary point
to the left by one place is equivalent to
dividing by two, so moving the binary point four places is equivalent to
dividing by 16. So when we put a period in the middle of the 8-bit
binary number, it is exactly the same as dividing by 16. We then have
the quotient to the left of the binary point, and the remainder to the
right of the binary point. Just convert each to hex, and we are done.
Notice that the only arithmetic we have to do is converting each chunk of four bits to the equivalent hex digit. The mental arithmetic involved is limited: we know that (1) we are adding one-digit numbers, (2) at most four of them, and (3) the sum will always be less than 16.
Watch the rest of Prof. Kurmas from Grand Valley State University on binary numbers and hex numbers. This is a version he edited for us. You watched the first 5 minutes for last time; watch the rest for today.
Even better, here's a video
with Tom Lehrer
singing New Math
. It's about 4 minutes long; you'll enjoy it.
We already know that every color in a computer is a combination of some amount of each of the three primary colors: red, green and blue. The amounts are always given in the same order: red, green, blue. The amounts are numbers from 0 to 25510, or, in hexadecimal, 00 to FF16. Each primary is expressed as a two-digit numeral in hexadecimal, using a leading zero if necessary so that the numeral is always two digits. Three pairs of hexadecimal digits completely specifies a color. Finally, the notation for a color always starts with a pound sign (#). For example, a color like (35, 230, 10) would be written #23E60A.
Experiment with defining a color numerically. In the form below, enter a color value in the syntax #RRGGBB and press return/enter. The box will change its background color to display the entered color value.
Now that we know how to represent a color, we can represent images. You can think of an image as a rectangular 2D grid of spots of pure color, each represented as RRGGBB. A spot of pure color is called a pixel, short for picture element, the atom of a picture. Pixels are better seen if you blow up an image several times; here are some examples. Click on the picture to enlarge it.
Every image on the computer monitor is represented with pixels. The images on a web page are saved in files that, in addition to the image data, contain information on the size of the image, the set of colors used, the origin of the image, etc. Depending on how exactly this information is saved, we refer to them as image formats. GIF, JPEG, PNG, and BMP are some of the well-known image formats. We will talk more about image formats below. For now, we will focus on the number of pixels and the representation of each pixel, and consequently, the file size of the image.
We said above that the amount of each primary color is a number from 0 to 25510 or 00 to FF16. It is no coincidence that this is exactly one byte (8 bits). A byte is a convenient chunk of computer memory, so one byte was devoted to representing the amount of a single primary color. Thus, it takes 3 bytes (24 bits) to represent a single spot of pure color.
With 256 values for each primary, we have 256 x 256 x 256 = 16,777,216 colors. Humans can distinguish over 10 million colors, so 24-bit color is sufficient to represent more colors than humans can distinguish. All modern monitors use this so-called 24-bit color. Some old monitors used 16-bit or 8-bit color, which were relatively impoverished, being only able to represent 65,536 colors (for a 16-bit monitor) or 256 colors (for an 8-bit monitor). Of course, a black-and-white monitor can only represent two colors, which could be called 1-bit color. An example is the Scottish terrier picture, above.
In an uncompressed file format, every pixel needs 24 bits (3 bytes) to be stored. Let's suppose you are going to take pictures of all your 30 class peers for a class website, using your iPhone4 camera. According to the phone specifications, its screen has 2592 x 1936 pixels, which amounts to about 5 million pixels, or 5MP (mega-pixels). Thus, if every pixel takes 3 bytes, and a photo with your camera has 5MP, to store the image you need 15MB (mega bytes). For all your peer photos, you will need 30 x 15MB = 450 MB.
Imagine now that you put all these photos online on your website, in one single page (using the attributes width and height to make them fit in one screen), and then you send the link to this page to your parents. They might have an average Internet connection (e.g. Verizon offers offers a 1-3 Mbs (mega bit per second) to non-FiOS subscribers).
The amount of time that it will take to load a page with all these pictures on your parent's computer can be calculated as below:
content size (450MB) x 8 bits/byte / 1Mbs = 3600 seconds or 1 hour.
If each of your photos have been only around 100KB instead (as we require in some of your homework assignments), then the amount of time to load all of them on the page would have been 24 seconds.
So, how do we get our images to be so small in size? There are two ways: resizing (decrease the number of pixels in the image by judicious cropping), and compressing (decreasing the necessary number of bits per pixel). We will discuss compression in the next section.
Short of making our images smaller (fewer pixels), what can we do to speed up the downloads? We can compress the files.
There are two classes of compression techniques:
We will look in detail at one kind of lossless compression, which is indexed color (GIF encoding), because it gives us a window into the kinds of ideas and techniques that matter in designing representations of information.
The idea behind indexed color is that if a particular color is used many times in an image, we can create a "shorthand" for it. In fact, if we limit the number of colors, each one can be assigned a shorthand. What will be confusing is that the colors are, of course, represented as numerals and so are the shorthands! For example, instead of saying (for the umpteenth time), color #D619E0, we'll just say, for example, color number 5. This will only work, however, if the shorthands really are shorter. They are, and we'll see exactly how much.
One way to think about indexed color is that we are creating a "paint-by-numbers" picture. We choose:
index | color |
---|---|
0 | #FF0000 |
1 | #FFFF00 |
You can see the general scheme at work: we create a table of all the colors used in the picture. The shorthand for a color is simply its index in the table. We will limit the table so that the shorthands will be at most 8 bits. Since the shorthands are all replacing 24-bit color specifications, the shorthand is at most one-third the size. In the example above, the shorthand is 1/24th the size.
Let's continue with the example. What is the file size if the image uses 4 colors, say red, yellow, blue and lime? In that case, the table looks like this:
index | color |
---|---|
00 | #FF0000 |
01 | #FFFF00 |
10 | #0000FF |
11 | #00FF00 |
As you can see, the shorthand is now two bits instead of one. Therefore, the 150,000 pixels require 300,000 bits or 300,000/8=37,500 bytes or about 37.5kB. Obviously, this is about twice the size of the previous example, since each shorthand is now twice as big. Nevertheless, it's still much smaller than the 450 kB uncompressed file.
What about the size of the palette? That's now twice as big, too. Four entries at 3 bytes each adds 12 bytes to the file size, which is a negligible increase to the 37.5 kB.
What's the pattern here? The number of colors in the original image determines the size of the palette, which determines the number of bits in each shorthand, which then determines the size of the file as a whole. The shorthand for a color is simply the binary numeral for the row that the color is in the table. For example, the color red in the last example was in row zero (00 in binary) and the color lime was in row 3 (11 in binary).
You can see that the number of bits required for each pixel is the
key quantity. This quantity is called bits per pixel or "bpp." It's
also often called "bit depth" so that the file size of an image is
just width x height x bit depth
, almost as if it were a
physically 3D box.
Finally, we can state the rule:
The bit depth of an image must be large enough so that the number of rows in the table is enough for all the colors. If the bit depth is d, the number of rows in the table is 2d.
Here's the exact relationship, along with the size of a 300x500 image:
bit-depth | max colors | file size of 300x500 image |
---|---|---|
1 | 2 | 18kB |
2 | 4 | 37kB |
3 | 8 | 55kB |
4 | 16 | 73kB |
5 | 32 | 91kB |
6 | 64 | 110kB |
7 | 128 | 128kB |
8 | 256 | 147kB |
Consider an image that is 80 x 100 (pixels).
In summary, you can reduce your image file size by using fewer colors. Of course, this may reduce the quality of your image. It's a tradeoff.
We've learned how indexed color works and how it affects file size. This is important not only for the theoretical understanding of why representations matter, but also for the practical usefulness of understanding how to reduce the sizes of your images. In this section, we'll review how to compute the approximate size of an indexed-color image. (Indexed color is one of the tricks used in GIF files, though GIF files use other tricks as well.) Why do we do this? Because it combines all the conceptual issues into one small calculation.
A key concept in the computation is the bit-depth of the image. Read on page 19 the definition of bit-depth. It's the number of bits necessary to represent the desired number of colors. Remember that the number of colors is 2d, where d is the bit depth. It's an exponential relationship. Adding just one bit to the bit-depth doubles the number of colors you can have.
Recall that the indexed-color representation comes in two parts:
Thus, our computation breaks down into two parts.
width * height * bit_depth / 8 |
num_colors * 3 |
To find the rough size of an image, we first determine the
bit-depth, then we compute the file size using the two formulas above.
(This is the rough
size because, remember, we are omitting some
fixed overhead and further compression techniques.) You can combine
them into one formula:
(width * height * bit_depth) / 8 + (num_colors * 3) |
Finally, because the file size will usually be large (thousands or millions of byte), we divide by 1000 or 1,000,000 to convert to kilobytes or megabytes, as appropriate.
We will continue to discuss file size calculations in lab and homework assignments.
For diving deeper into JavaScript, please read Chapter 1 of the book Head First Javascript Programming.
So far we've learned two languages: HTML and CSS. Today, we start learning the third of the three languages taught in this course: JavaScript. We will just begin talking about it, so don't worry if you don't understand everything immediately.
JavaScript is a programming language for making dynamic web pages, i.e., pages that can interact with the user and vary each time they are loaded. Although the names are similar, JavaScript is not the same language as Java, which is the programming language used in CS111 (until Spring 2014).
We will begin our investigation of JavaScript by presenting a series of simple but working examples. We'll show you the code, but you should not try to completely understand it at this point. Instead, you should read it, make some guesses as to what various things might mean, and try to get the gist of what is happening without worrying about all the details. Later, after we have explained some of the notation and its meaning, we can go back to these examples and understand them more fully.
(This is a little like trying to learn a foreign language by walking around the country, listening to the words people say and observing what they do. You'll miss a lot, especially at first, but it's way more interesting than trying to start by reading a dictionary and memorizing vocabulary and syntax.)
Here, we gather data in the form of the user's name, then we compute a response string by gluing their name onto the end of a common greeting, and finally output the result with a popup window.
We would probably never do this greeting in a real website, but someone's name could be filled into a form, added to a roster, guest list or signup sheet, emailed to someone, ...
var name =
in the first line and execute the code again.
What happens? Can you explain it? What roles are name
and response
playing?
This is almost the same, but we tell them the length of their name.
Again, not useful in itself, but it's nice to know that we can find out the length of a string. For example, we can limit the length of people's posts in an application like a Facebook wall.
var aName =
in the first line and execute the code again.
Can you explain what happens this time?
Open the View > Developer > Javascript Console menu item.
Anything interesting there? (Ignore the complaint abour Google code prettify.)Here, we ask for a number and use it in a calculation.
You probably saw a very long number as an answer. For example,
the result for 130, is 59.090909090909086. It would be nice to round
it off to the nearest integer. And you can. If you were to compute
Math.round(59.090909090909086)
you would get the result 59. Can you find a way to
put the Math.round
calculation in the code above, so that it gives you a
rounded value in the alert window. (That's hard at this stage of your learning.)
Here, we ask for the person's name, quest, and favorite color. We then modify the box below, which was created with the following HTML and CSS:
<div id="grail"> Your name here </div>
#grail { border: 5px solid gray; color: gray; width: 50%; }
OK, here is the javascript code that will change the box above:
Javascript, just like CSS, is a different language from HTML. You might remember that HTML is responsible for defining the structure of a document, CSS for defining its appearance, and Javascript for defining the behavior of a page. Similarly to the way CSS interacts with HTML through inline styles, document-level styles and an external stylesheet, Javascript has the same three ways, but with a different syntax.
Here is the code:
<button id="greeter1" type="button" onclick="alert('hi there!');">Greet me</button>
Notice the JS code in the onclick
attribute; there
are also other attributes where JS code can be put.
<script>
. Differently from CSS, where
the <style>
tag could be only included in
the <head>
part of the document, in Javascript
you can put <script>
either in
the <head>
or anywhere inside the
the <body>
.
A good practice is to put the code just before
the closing tag for body
. Here is an example:
<body> ... <button id="greeter2" type="button"">Greet me</button> ... <script> function sayHello() { alert('hello!!'); } $("#greeter2").click(sayHello); </script> </body>
In the preceding, you'll see that the button has no JavaScript
code at all; the code is in the script
tag. However,
there's a slightly more elaborate mechanism for defining and
attaching the code. We'll get to these later in the course.
script
element above would go in
a separate file and we simply load it using an alternative form
of the script
tag. Here is an example:
<script src="myJScode.js"> </script>
We'll be using all three ways in our examples, but like with CSS, if you were creating a larger website, you should avoid inline JS. You should use mostly external JS (when shared across pages) and document-level JS.
In the following sections, we'll get a first look at various facets of the language. There's more to say about all of these, but this brief tour will help you see how these different aspects of the language fit together. After all, when you learn a natural language (e.g. French or Chinese), you don't try to learn all the verbs first, before tackling any of the nouns or adjectives.
Variables are just named storage locations for a program. We used them
in the examples above to store stuff that we computed (such as weight in kg)
or stuff that came from the user (such as their name or height).
The technical name for stuff
might be data
or values
.
You can put a value into a variable, using an assignment statement, like these examples:
var name = "Harry Potter"; var weight = 150;
Note that this use of the equals sign is very different from how it is used in mathematics. In math, the equals sign (=) is an assertion that two things have the same value.
In computer science, the equals sign means:
Put the value that you see on the right of the equals sign into the storage location that is mentioned on the left of the equal sign.
Because the equals sign doesn't mean equality, we often pronounce it
differently. Instead of saying weight equals 150
, we
say weight gets 150,
short for gets assigned the
value
.
After a variable has been assigned a value, the value can be retrieved
and used just by giving the name of the variable. For example, the
following two statements retrieve the values
of name
and weight
, perform an operation on them,
and then assign the new values to two new variables
called len
and kg
.
var len = name.length; var kg = weight/2.2;
We will return to the concept of variables in future lectures.
alert()
FunctionThe first few examples accomplished the reporting of the
response by use of the alert()
function. It looked like
this:
alert(response);
This built-in function will pop up a window that displays whatever
value is supplied to the alert()
function by placing it in
the parentheses. In this case, it reported the value that was in the
variable named response
. In earlier examples, we
saw alert()
with an expression inside the parentheses. This
expression is evaluated and then alert()
reports the
result.
Alerts are annoying in working code, but they are very useful when testing and developing code, because they stop the execution of the program until you acknowledge them:
Some terminology: A statement like alert("foo")
is an example of a
function call or function invocation,
where alert
is the name of a standard JavaScript function and
the expression in parentheses (in this case, "foo"
) is
the argument of (that is, input to
) the
function. We'll learn more about functions later, but
knowing alert()
is useful right away.
console.log()
FunctionVery similar to alert()
but much less annoying
is console.log()
. It's used in almost exactly the same way:
you put a value (or a variable containing a value) into the parentheses,
and that value gets reported. However, the difference is
that alert()
demands that the user acknowledge it,
while console.log
writes it to a hidden place that only web
developers know about, and keeps going, so the user is not annoyed or
delayed by the message.
The console is also where the browser writes error messages. It writes them there so that the muggles won't see them and worry about them. As of this writing, here's how you can find them:
Try it! Click the button below to execute those
two console.log
statements and then look for them in the
console of your current browser.
We will use console.log
and alert
a lot as a
way of getting some insight into what our code is doing and to help
understand it and debug it. So it's worth becoming comfortable with
finding and looking at the console.
You should always add comments to your JavaScript code to explain its purpose. As shown in the example below, JavaScript supports two kinds of comments:
/*
and ending with */
that may span several lines.
(This is the same form as a CSS comment.)
//
and goes to the end of the line.
<script> /* (Block Comment) Pop up an alert box with a message making sure that you all are awake because at this time of day, you might be sleepy. Of course, a lot of it depends on what you were doing last night... */ alert("Hey you there -- wake up!"); // (Line Comment) The user must be awake if we reach this point! console.log("User has woken up"); </script>
In addition to explaining the code, we can use comments to comment out
a line of code that we don't want to run (sometimes when
we are debugging to find where the error lies).
JavaScript programs manipulate values. The two basic kinds of values we've seen today are:
"
) or
the single quote character ('
):
"Harry Potter" 'grail'
An expression is a JavaScript phrase that
denotes a value. The simplest numeric expression
is a number itself. We can also create more complex numeric
expressions using the numeric operators
+
, -
, *
, /
and %
:
Expression | Value |
---|---|
27 |
27 |
27 + 10 |
37 |
27 - 10 |
17 |
27 * 10 |
270 |
27 / 10 |
2.7 |
27 % 10 |
7 |
The % operator is the remainder or modulus operator. Its value is the remainder when the first number is divided by the second. Here is an example of when it can be used. An average size woman might be 64 inches tall. To find the number of feet, we divide 64 by 12 and round down, to get 5. To get the number of leftover inches, we divide 64 by 12 and take the remainder, using the % operator. That gives us the 4. The remainder operator is more useful than you might think. But don't worry about it; you can think of it as a weird kind of division.
For the most part, JavaScript follows the algebraic precedence rules that you learned in school years ago.
JavaScript follows these rules, so your prior experience will serve as
a good guide. There are a few new operators that you'll learn, such as
the %
or remainder operator, which has, unsurprisingly, the same
precedence as division.
Try to guess what the following will evaluate to:
Literal strings can be denoted by either single or double quotes, which gives you some flexibility about how to handle awkward situations such as quotation marks inside a string:
Expression | Value |
---|---|
"Let's play Tag!" |
Let's play Tag! |
'Not "it"!' |
Not "it"! |
These examples show that you can put a single quote inside double quotes and a double quote inside single quotes.
The main operation on strings is the concatenation
operator, +
:
Expression | Value |
---|---|
"CS" + "115" + " rocks!" |
CS115 rocks! |
"Harry" + "Cho" |
HarryCho |
We saw this many times in the examples, as we created our response text out of literal strings, values supplied by the user, and computations from them. Concatenation is also useful for splitting strings across multiple lines. Let's say we want to display a string that represents a table. If we try to fit it in one line, it may look something like:
alert("id,name,age,height(ft),0,Amy,5,4,1,Ben,9,6,2,Cindy,5,5");
With all of those numbers, it can be hard to parse the information when reading the code. Unfortunately, we can't jump to the next line in the middle of a string, but we can use concatenation:
alert("id,name,age,height(ft)," + "0,Amy,5,4," + "1,Ben,9,6," + "2,Cindy,5,5");
parseInt()
and parseFloat()
FunctionsWe'll see that there are many situations where it's necessary to
convert from a string of digits to numbers. The
parseInt()
and
parseFloat()
functions are used for this purpose. The first one is used with integer numbers
and the second one with numbers with a fractional part (after the
decimal point).
The function takes an (optional) second parameter that says what number base to use. We'll typically use 10 for decimal numbers.
Expression | Value |
---|---|
parseInt("243",10) |
243 |
parseInt("cs115",10) |
NaN (Not a Number) |
parseInt("3.141",10) |
3 |
parseFloat("3.141") |
3.141 |
The second argument to parseInt
is the radix
or base
that you want to use. It can be 10 for decimal
number, 16 for hexadecimal numbers and 8 for octal numbers (base 8).
It's a good idea to supply this value, to avoid JavaScript inadvertently
doing the wrong thing.
You can test any of the above expressions here:
prompt()
FunctionLike alert()
, invoking the prompt()
function
pops up a small window, but (1) it also allows the user to type in a
string and (2) the string entered by the user is returned as the value of
invoking the prompt()
function.
For example, the following statement prompts the user for a first name and then stores it in a variable.
The prompt()
method is not the best way to get input in Javascript but it is OK for now. Better ways are to use HTML forms,
or the jQuery library, but we can ignore it for now.
The first argument of the prompt()
function is displayed
above the input area of the prompt box.
The prompt()
function takes an optional second argument
that, if supplied, is used as a default value for the input area.
For example:
Using a default value makes it easier on the user when the default is right, but allows him or her to correct (override) the value when it's wrong.
prompt()
The result of invoking the prompt()
function
is always a string, NOT a number, even if it's
all digits, so it's necessary to use
parseInt()
or parseFloat()
to convert sequences
of digits to numbers. Compare the following two examples:
In this first example, we just use the +
operator on the
strings returned by prompt()
:
However, in this example, we first convert the strings into numbers (a
float for the first, and an integer for the second), then apply
the +
operator:
Note that if we never need the strings a
and b
, we can take the value of prompt
and
immediately convert it to a number using parseInt
, so the
second example can be written more succinctly like this:
You have already learned a lot of JavaScript. For diving deeper, please read Chapter 1 of the book Head First Javascript Programming.
So far, the Javascript code we have seen has been executed unconditionally. When we wrote a sequence of statements, those statements were executed one after another, in order.
Many programming tasks, however, require conditional control,
that is, the ability to react differently based upon some condition. For
example, consider the task of assigning students' letter grades. Depending
upon what the student's average is, a different letter grade must be
assigned (e.g., 93 to 100 is an A, 86 to 93 is a A-, etc.). In this
lesson, you will be introduced to booleans,
truth values that represent a decision, and to the
if
statement, which
performs conditional execution based on a boolean value.
Based upon some condition, an if
statement can choose among alternative
sequences of code to execute, or even choose to execute no code at all.
Some examples, just to get us warmed up:
The following code asks a yes/no question ("ok" and "cancel") and does something on "yes":
The following code asks the same yes/no question and does something either way:
Those examples both used the confirm()
function, which is
like prompt()
except that it returns a boolean
value, namely either true
or false
.
Before today, the JavaScript values we have seen are numbers and strings.
Today we meet the logical values true
and false
.
These two values are called booleans.
They are named after George Boole, a 19th century English mathematician
who developed an algebraic framework for reasoning about the manipulation
of truth values.
A boolean expression is any expression whose value is
a boolean value. The simplest boolean expressions are the boolean
literals true
and false
, which can be
written directly in a JavaScript program. (They're not strings, so
don't put them in quotation marks.) However, in a program, it is more
typical for boolean values to be generated as the result of
comparisons or other operations.
For example, relational operators on numbers produce boolean values. Put a number into the box below and click the "evaluate all!" button to see the boolean outcomes.
Expression | Value | Notes |
---|---|---|
num | the meaning of the comparison in the first column | |
num < 50 | less than | |
num <= 23 | less than or equal to | |
num == 57 | equal to (== tests equality, but = performs assignment!) |
|
num != 17 | not equal to | |
num > 110 | greater than | |
num >= 42 | greater than or equal to |
Here are some examples of boolean expressions involving strings. Again, type a string into the box below (or use the default) and click the "Evaluate all!" button.
Expression | Value | Notes |
---|---|---|
str | additional info about the comparison | |
str == "CS115 rocks!" | == also tests equality of strings |
|
str != "Grover" | != also tests inequality on strings |
|
str <= "CS111 is cool!" | Strings are compared lexicographically (in dictionary order) | |
str > "CS24 isn't a course" | Careful! Numbers in strings are also treated lexicographically. | |
str.length == 12 | s.length returns the number of characters in s |
|
str.indexOf("115") == 2 | s1.indexOf(s2) returns the first (0-based) index of s2 in s1 |
|
str.indexOf("Rocks") == -1 | indexOf returns -1 if string isn't found |
|
str.charAt(1) == "S" | s.charAt(i) returns the character
at (0-based) index i in s (In JS, a character is just a one-character string.) |
if
StatementsConditional execution refers to the ability to execute a
statement or sequence of statements only if some condition holds. The
simplest form of conditional statement involves only one possible
action. For example, displaying a message when the value of a variable
is "Grover"
. However, conditional execution can also
involve alternatives based on the same or related conditions. For
example, we might want to display one message if the variable value is
"Grover" and another message if it isn't. This type of conditional
execution in JavaScript is performed using if
statements.
The following example uses an if
statement to
greet Grover specially. Let's say we have gotten the name of the
visitor stored in a variable first_name
. The symbol
==
is used in JavaScript to test for equality
(remember, just one =
is for assignments!).
The following example uses the other form of an if
statement to greet Grover and to be a bit hostile to everyone else.
In the first example, an alert box is displayed only if the
condition (first_name == "Grover")
holds. If the
condition does not hold, then nothing special happens: the next
JavaScript statement (if there is one) is evaluated.
The second example includes an else
case which
specifies the action to be taken when the condition does not hold
(i.e., first_name
is not equal to
"Grover"
).
In an if-else
statement, exactly one of the two
actions will be executed.
In general, an if
statement looks like this:
if (BOOLEAN_EXPRESSION) { // This is called the "test expression" STATEMENTS_IF_TRUE // This is called the "then arm" or "then branch" } else { STATEMENTS_IF_FALSE // This is called the "else arm" or "else branch" }
The else
part is optional.
Try an example here, testing whether a number is positive.
The test in an if
statement can be any boolean
expression, that is, any expression that evaluates to either
true
or false
.
Add an else
case to the if
statement
so that it displays an appropriate message in the event that the
number is not positive. Test your modified page on various inputs and
list the results. What will be displayed when the number is 0?
if
statement
are mandatory. So you cannot write the following:
if input_num > 0 { // missing required parens for test expression
alert("the number is positive");
}
if (input_num > 0) alert("the number is positive"); // No braces for then arm
However, we recommend that you always use braces to avoid bugs that come from adding more statements to the then arm.
Boolean values can be tranformed and combined via three logical operators:
!
(pronounced "not") is a unary (one-argument) operator that negates a boolean value:
(! true)
is false
and
(! false)
is true
. If sunny
is a
variable that contains a boolean value, you might use the not
operator like this:
var hot = true; var sunny = true; if( ! sunny ) { alert("bring an umbrella"); }
&&
(pronounced "and") is an infix binary (two-argument) operator that returns
true
if and only if both arguments are true
; otherwise it returns
false
. If sunny
and hot
are
variables that contain boolean values, you might use the and
operator like this:
// assume you have already assigned values to sunny and hot if( sunny && hot ) { alert("wear a hat"); }
||
(pronounced "or") is an infix binary (two-argument) operator that returns
true
if and only if at least one of its arguments
is true
; otherwise it
returns false
. If cold
and windy
are variables that contain boolean values, you might use the or
operator like this:
// assume you already have assigned values to cold and windy if( cold || windy ) { alert("wear a coat!"); }
The meaning of the three logical operators is summarized by the following tables, where inputs are shown in blue like this and outputs are shown in red like this:
Negation (not) | Conjunction (and) | Disjunction (or) | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
Here are some examples of these operators in action. Type a numeric
value into the box and click the Evaluate all!
button to see the
outcome.
Expression | Value | Notes |
---|---|---|
x | ||
(5 <= x) && (x <= 20) | x is between 5 and 20 (inclusive) |
|
(x < 10) || (x > 25) | x is not between 10 and 25 (inclusive) |
|
!((15 <= x) && (x <= 30)) | x is not between 15 and 30 (inclusive) |
One way to think about these conditionals on numbers is by using
the number line, where &&
means intersection. This
is because both conditions have to be true, which is when the regions
overlap. For example, consider modeling the expression:
(X >= 5) && (X <= 30)
b
is between a
and c
(inclusive), in JavaScript you write (a <= b) &&
(b <= c)
. Unlike in mathematics, the JavaScript expression a
<= b <= c
does not have the same meaning. Why? Consider the
evaluation of the following example:
7 < 3 < 10 // You might think this expression is false, but it's true. Why? => (7 < 3) < 10 // Relational operator associates to the left => false < 10 // (7 < 3) is false => 0 < 10 // In a numerical context, false is treated as 0, true is treated as 1 => true // (0 < 10) is true!
!
has a higher precedence than &&
, which has a higher
precedence than ||
. For example:
! true && false
is parsed as (! true) && false
(which evaluates to false
)
and not !(true && false)
(which evaluates to true
)
Although &&
and ||
have a lower precedence than
comparison operators, !
has a higher precedence. So
! 2 < -1
evaluates as (! 2) < -1
(whose value is false
) and not ! (2 < -1)
(whose value is true
).
Moral: Use explicit parentheses if you have any questions about operator precedence!
Suppose that CS110 lectures are held between 8:30am and 12:20pm.
Suppose that hour
denotes the hour in military time (in the
range 0 - 23) and min
denotes the minute (in the range 0 -
59). Our goal is to write a single boolean expression involving
hour
and min
that displays an alert box
with true
for times during CS110 lectures
and false
for other times.
alert
so that it works.
if
StatementsIn its simplest form, an if
statement can decide whether
or not to execute a sequence of statements. For example, in one of our
first examples of an if
statement, repeated here, the code
will either display a message on the condition that the name is
"Grover"
, or otherwise it will do nothing at all.
if (first_name == "Grover") { alert("Hello Grover, great to see you!"); }
Using an else
case, an if
statement can
decide between two alternative sections of code, as in the example where a
different message is displayed for "Grover"
and any other
name:
if (first_name == "Grover") { alert("Hello Grover, great to see you!"); } else { alert("Who the heck is " + first_name + "?"); }
There are situations, however, in which more than two alternatives must be considered. For example, suppose you were to write code for assigning letter grades in a class based on the student's final class average.
if (grade >= 90) { letterGrade = "A"; } else if (grade >= 80) { letterGrade = "B"; } else if (grade >= 70) { letterGrade = "C"; } else if (grade >= 60) { letterGrade = "D"; } else { letterGrade = "F"; }
We call this a cascading if statement. The
term cascading refers to the way that control cascades down the
statement like water down a multi-tiered waterfall. The topmost test is
evaluated first, in this case (grade >= 90)
. If this test
succeeds, the corresponding statements are executed and the whole
statement is done. If not, control cascades down to the next
if
test, in this case (grade >= 80)
. In
general, control cascades down the statement from one test to another
until one succeeds or the end of the statement is reached, possibly with a
final else
.
Augment your solution to Exercise 1 (from Part 1) so that there are three possible messages displayed, identifying whether the number is positive, negative, or zero.
This exercise shows how to generate different pronouns in a madlib based on the gender of the subject.
In the execution box below insert the statement that will use “his” if the person identifies as male and “her” if the person identifies as female, and “their” for anything else.
if
StatementsThe braces about the then and else branches of
an if
statement allow any amount of JavaScript code to be
under the control of the conditional — even other if
statements. This has exactly the effect you would expect: the code
comprised by the inner if
statement is conditional on both
statements. Let's outline an example, where we categorize something by
whether it is positive or not and whether it's even or not. That gives
us four possibilities:
if( even ) { ... if( positive ) { // 1. positive even } else { // 2. non-positive even } ... } else { ... if( positive ) { // 3. positive odd } else { // 4. non-positive odd } ... }
Of course, the inner blocks of code don't have to consist solely of the
inner if
statements. As indicated by the ellipses in the
outline above, the inner blocks can have additional code precede or
follow the inner if
statements. Indeed, that elided code
could include other conditionals!
This exercise shows how to assign the correct honorific based upon a person's gender and age. Females under the age of 18 are referred to as Miss, while those 18 and over are referred to as Ms. Similarly, males under the age of 18 are called Master, while those 18 and over are called Mr.
In the execution box below insert the statements that will assign the correct honorific.
rock
,
paper
,
scissors
)
and both players announce their choices at the same time.
The rules are:
rock
beats scissors
;
scissors
beats paper
;
paper
beats rock
;
p1
and
p2
each hold one of the three strings
"rock"
,
"paper"
,
or "scissors"
for player 1 and player 2, respectively.
Flesh out the program so that it pops up an alert box displaying
players tie
if the players tie.
player 1 wins
if player 1 wins
player 2 wins
if player 2 wins
This material was prepared by Gabriela Lanza '14, a long-time tutor for CS110, MAS major, and passionate website developer. Visit her beautiful website.
This semester, we've learned the nuts and bolts of web design: HTML, CSS, JavaScript (and we'll learn jQuery starting soon). But nuts and bolts don't tell you how to make something, and whether the result will be both functional and usable. This falls generally under the umbrella of usability, or user-centered design, which means, according to usability.gov designing your website so that users can effectively, efficiently and satisfactorily achieve their goals. We return to that topic today, which is particularly important as we prepare for implementing your websites.
Usability isn't an easy thing to master. There's no magical formula that you can follow to make your site user-friendly. There are, however, some guidelines that will make your site much easier for your users. These rules are based off of user interface design (such as interactive applications), but can easily be adapted to websites:
Responsive web design is certainly one of those things that falls under the umbrella of usability, but is so important that it deserves its own section. Making a website "responsive" means designing your website so that it is adaptable and accessible to users on any device.
Think of the multitude of devices out there today: computers with large-screen monitors, big laptops, small laptops, tablets, e-readers, phones, and many more. If users from any of these devices cannot get the information or achieve the tasks they need, your website loses a lot of effectiveness. The below chart shows the growth of mobile phone use in accessing the Web.
And that's just phones.
Well, it's much easier to show what a non-responsive website looks like. For example, the CS110 Schedule page of the old website (before Spring 2014) was not responsive:
At smaller screen widths, the user cannot see the whole schedule at once, which is a problem in and of itself. And because the menu on the side is fixed, it overlaps the menu when you try to scroll sideways to see the rest of the schedule. The spigotdesign.com site, however, is designed to be responsive. Try visiting the site and re-sizing your browser. The following image shows what the site looks like on a computer, on a tablet, and on a mobile phone.
Elements have been adjusted in size, moved, and even taken away entirely to adapt for different screen sizes. There are really no hard-and-fast rules for making a website responsive. Much of the process of responsive web design involves designing for big screens, testing for different screen widths, and then adjusting your site accordingly. There are, however, some helpful guidelines for designing for smaller screens.
In order to adapt your website to different screen widths, you first must determine your screen's width, then apply certain rules to
your screen under those circumstances. This is done with a media query. A media query is like an if
statement in
JavaScript: if your screen is within a certain range, then apply the CSS rules.
Read the CSS code below and try to guess what will happen to the div elements of class colorblock
,
if you resize the page.
.colorblock { width: 100%; max-width: 850px; height: 30px; background-color: red; } @media screen and (max-width: 700px) { .colorblock { background-color: blue; } } @media screen and (max-width: 500px) { .colorblock { background-color: green; } }
Then, try it out by playing with this page.
Notice that:
In the example above, 700px and 500px become special numbers. In the web design world, these are called breakpoints, since they are screen widths where things on the page change. In general, it is good to have as few breakpoints as possible - it can be confusing if things are constantly changing when you resize your screen, and difficult to maintain. Thereis a lot of debate in the web design world about the best breakpoints for your media queries, but, generally, safe bets are 768px and 480px, the screen widths of an iPad and an iPhone.
On another note, you'll notice that the div itself is responsive and resizes along with the screen. This is because we gave it a 100% width and a max-width of the largest pixel size we want our element to be. This is a great trick for making images responsive. Percentage widths are one of your biggest tools in making a site responsive; instead of fixed-pixel widths, use percentages of the screen size. Other tricks include:
display: none
display: inline
or
display: inline-block
to display: block
display: inline-block
is a useful CSS rule we didn't talk about in class. It allows elements to act as block elements
in terms of size and containing other elements, but is treated like an inline element for display purposes, allowing it to
line up side-by-side with other inline elements.Besides that, just try things! You'll never know what works until you spend some time finding out what doesn't first.
Here's a responsive version of the CS110 schedule. Try resizing your browser, then read the explanation.
Here's a fancier responsive version, which automatically generates day labels in the narrow version.
You're probably thinking by now, Wow, this sounds like a lot of code! But don't be afraid! There are tons of frameworks on the Web that make creating responsive websites easy. Most of these are built on something called a fluid grid, which, in most cases, takes care of the responsiveness for you. One of my favorites, and an industry standard, is Bootstrap, a framework built by two software developers at Twitter. If you plan on continuing in web design after this class, or if you're just interested, check it out.
Imagine that your web page would like to be aware of the current data. You could be prompting the user for time-related values such
as month
, hour
and minute
but it would be better to
dynamically compute the date and determine, say, whether it is spring or lunchtime and so
forth. We'll now see how to do that using the computer's clock instead
of prompt()
. Note that this is not the processor (CPU)
clock that keeps a tempo for the hardware, but the conventional time,
day and date
clock.
We can display the current date and time on a web page, using some built-in JavaScript to access the computer clock, and the string concatenation operators we already know. For example, the following box displays the current date and time, as reported by your computer:
You loaded this page on at
By the way, the HTML code for that box above is as follows:
<div id="date_today"> <p>You loaded this page on <span id="date1"></span> at <span id="time1"></span></p> </div>
Notice that we have two spans with CSS id's. This will make it easy for us to create JS code that will insert date and time values in these two spans using their id's. For example, executing the following JS code would assign a value to the first span box equal to January 1st of the year Wellesley College was founded!
document.getElementById("id").innerHTML = "01/01/1870"
If you reload this page, you'll notice that the date and time change to the current time of your reload. (You may see a slight flaw in the appearance (missing a leading zero in the minutes or seconds); we'll fix that later.)
Before we explain the JavaScript code that can do this,
we need to understand how time is represented in JavaScript.
We begin by creating a Date
object:
var dateObj2 = new Date(); // The name tries to convey that this is a date object
Similarly to what we've seen before, the keyword var
in var dateObj2
creates a new
variable box named dateObj2
for storing a
value. What we haven't seen before is the keyword
new
, which causes a
JavaScript object to be created. In this case, the object
represents the current date and time. JavaScript comes equipped with lots
of pre-defined object types like Date
. For those who have the time and interest, there is optional reading for a
general description of objects. For now, you can
think of an object as a collection of information, not just a single value.
We can extract information from a Date
object by
invoking methods on it. The table below shows some of the
important methods that Date
objects understand. The elements
of the Value column are dynamically computed by evaluating the
JavaScript expressions in the
Expression column, so reloading the page will update
these appropriately.
Expression | Value | Notes |
---|---|---|
dateObj2.getFullYear() | Full year | |
dateObj2.getYear() | Avoid this! Varies from browser to browser | |
dateObj2.getMonth() | 0=Jan, 1=Feb, ..., 11=Dec | |
dateObj2.getDate() | 1 to 31 | |
dateObj2.getDay() | 0=Sun, 1=Mon, ..., 6=Sat | |
dateObj2.getHours() | 0 to 23 | |
dateObj2.getMinutes() | 0 to 59 | |
dateObj2.getSeconds() | 0 to 59 | |
dateObj2.getTime() | Milliseconds since Jan 1, 1970 (the "epoch") |
You'll note that all of these methods "return" numbers, extracting one piece of information out of the collection that the object comprises. Usually, they are pretty intuitive:
.getMinutes()
returns a number from 0 - 59.getFullYear()
returns a number like 2015.getHours()
returns a number from 0 - 23, which is
intuitive for everyone in the world except for Americans..getDate()
returns a number from 1 - 31, depending on
the month..getDay()
returns a number from 0 - 6, depending on
the day of the week. You might think it would be 1-7, but the
designers of JavaScript chose to number the days of the week starting
at zero. Odd, but bearable..getMonth()
returns a number from 0 - 11, depending
on the month. This is deeply unintuitive for anyone except computer
scientists, who are accustomed to things being numbered starting at
zero. And they still get tripped up by things
like .getMonth()
Later, when we talk about arrays, we'll try to justify why certain things are numbered starting at zero. But for now, we'll just understand that these are what we have to work with.
W3 Schools has a complete list of
methods for Date
objects.
Let's discuss the notation for objects. First, imagine that we create two Date objects, a few seconds apart:
var obj1 = new Date(); alert("obj1: "+obj1); var obj2 = new Date(); alert("obj2: "+obj2); var sec1 = obj1.getSeconds(); var sec2 = obj2.getSeconds(); alert("seconds in obj1: "+sec1+" and in obj2: "+sec2); var diff = sec2 - sec1; alert("difference is "+diff);
You can think of each new Date()
expression as taking
a snapshot
of the computer clock, recording those values at that
moment in time.
Our mental picture of this example might be like this:
Because objects contain a collection of information, possibly quite large, we picture it as a round thing. (Here, each object is a light blue circle.) We can store these things in variables (labeled boxes), just like we store anything in variables, including numbers, strings, and booleans. The other variables just contain numbers; we're used to those.
Given an object in a variable, we use a method to do something with it (such as extracting some data) using the following syntax:
var_containing_object.method_name(args);
The dot is important: it separates the object (or a variable containing an object) from the method that is being used. Some methods need additional arguments; we haven't seen any yet, but we will later in the course. For now, we'll just see an empty pair of parentheses, but these are required.
The examples we saw above fit that pattern:
var sec1 = obj1.getSeconds(); // get seconds out of obj1 var sec2 = obj2.getSeconds(); // same, but for obj2
Now let's return to our date and time display. A date object contains a collection of information about the date and time, but for human-readability, we will need to format that data in some conventional way, using, for example, slashes and commas to separate the various numbers.
Here is the date, formatted as is done in the United States and a small handful of other countries. This box has text in Spanish, unlike our earlier box:
Ha cargado esta página en a las .
The HTML for that box above is just this:
<div id="date_today2"> <p>Ha cargado esta página en <span id="date2"></span> a las <span id="time2"></span></p> </div>
Here is some JavaScript code that inserts the formatted date and time
into the box above. (Note the two empty span
elements with
IDs that we'll use below.)
// create a date object, representing this moment, and store it var dateObj = new Date(); // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; // Add 1 because Jan is 0, etc. var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // format info about the time var the_hour = dateObj.getHours(); var the_minute = dateObj.getMinutes(); var the_second = dateObj.getSeconds(); var current_time = the_hour + ":" + the_minute + ":" + the_second; // insert the formatted strings into the document. document.getElementById("date2").innerHTML = current_date; document.getElementById("time2").innerHTML = current_time;
(You can also view the page created by embedding this code in a complete HTML file for displaying the date.)
Let's examine the code. It is a sequence of JavaScript statements,
which will be executed sequentially by the browser. The first statement
(line 2) creates a Date
object representing the current date
and time, and stores it in a variable named dateObj
. The
next block of statements (lines 4-8) extract components of
this Date
object and piece them together to construct a
description of the date, like 10/13/2015. The next block of statements
(lines 10-14) similarly extract components of this Date
object and piece them together to construct a description of the time,
like 17:22:44.
The final document.getElementById("id").innerHTML
statements each inserts a string into the span with the given ID, thereby
displaying the date and time on the page. The surrounding text is
fixed. You've seen this sort of trick in lab; we'll do more with this
later in the course. Here's another copy of the HTML code they inserted
it into:
<p>Ha cargado esta página en <span id="date2"></span> a las <span id="time2"></span></p>
Practice the behavior of the code by using the execution box below. Change the code to display the date in the format that most of the world uses, namely, DD/MM/YYYY.
Remember:
<script>
tags. They are implied.
.html()
code with
an alert()
, so that the code is obvious when it's running.
Dates are one kind of object. In JavaScript, an object is a kind of value that has two important characteristics:
width
, height
, margins
,
color
and much more. Date objects wouldn't have that.
Both properties and methods are selected from an object using dot notation. In the examples above, we only used methods. You can tell we're using a method rather than selecting a property by the presence of parentheses after the name.
We've used the example of dates to teach you about:
Lots of other objects and methods exist in JavaScript.
Here's another method, this time on numbers,
the toFixed()
method:
We'll show you how it's implemented a bit later, but for now, please take it on faith.
How would you print the name of the month 10 months from now?
All along, we've created Date objects as a snapshot of the current
computer clock, but we don't have to. We can create a Date object for any
date of our choosing, just by giving a string to the Date()
function. Here's a bit of code that will tell us what day of the week
Christmas falls on in 2015:
Note, though, that if you mess up the syntax of the string describing the date and time, you'll get an invalid Date object and nothing good will happen:
You can fix the above by removing the "am" part; remember that "13:15" would be "1:15pm", so the "am/pm" part is unnecessary.
Date
object is used to manipulate dates and time.
The expression new Date()
creates a
Date
object representing the current date and time.
Methods like getMonth()
and getHours()
are used to extract components from a Date
object.
In this reading, we'll learn a very powerful aspect of programming languages, namely the ability to define and use our own functions. We have used many "built-in" functions, such as alert
and parseInt
; now we will be able to build our own.
Why is this so important? Consider a function
like parseInt
: it does something that is useful in many
situations and we can use it without understanding how it works
(its implementation). If we are involved in a large software
project, someone else on our team can implement a function that
we can use, and vice versa. We can use their code without
understanding all the details of its implementation, which saves us
mental effort. This idea also allows the team to be more efficient than
if each person had to understand everything.
This is another manifestation of the idea of modularity. A function is a kind of module. It can be defined in one place and used in many places, just as a single CSS rule can be defined in one place and used in many places. Furthermore, if we discover a bug in the function, its definition can be fixed and every user of that code gets the improvement. This is similar to modules in the real world, where if you drop your headphones into the bathtub and short them out, you can replace them without having to replace your music player, too.
So far, this may seem pretty intuitive; I certainly hope so. Nevertheless, functions can be pretty complex at times. To explain them, I will use three metaphors:
We can even combine all these metaphors and imagine a function as kitchen with a chef inside. The users of the kitchen don't need to know anything about the workings of the kitchen. All they need to know is that they give it certain inputs (eggs, flour, cocoa, or maybe just money) and outputs are generated (a chocolate cake).
Defining a function is like making up a recipe. We have to give it a name and list the steps that need to be done. Once the recipe is defined, anyone can use it.
Let's look more precisely at the syntax of a function definition:
<script> // comment about your lovely function function functionName() { . . JavaScript code to define the function goes here . . }
To define your own function you need:
It is also considered good practice to put a comment before the function describing what the parameters are for, what value gets computed if any, what actions are performed, etc. Anything that isn't obvious from the code. (Comments that just repeat the code in English are bad.)
Here's an example:
function chocolateCake() { // step 1, combine sugar and flour in a large bowl // step 2, add two large eggs // ... // frost and serve }
The chocolate cake example is a little silly, because we don't know any JavaScript for the definition. Here's a better one that formats the date on the page. You'll recognize this code from our recent discussion of the Date object, though I've abbreviated the code a little.
function formatDate() { // create a date object, representing this moment, and store it var dateObj = new Date(); // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; // Add 1 because Jan is 0, etc. var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById("date2").innerHTML = current_date; }
Now we come to an interesting situation, because we have defined a function, but how do we use it. Here, I'll use the metaphor of a machine: we have a blender, but how do we turn it on?
Computer scientists have a special word for running a function (making
it do its job), and that is invocation. (Like invoking the
gods.) We say that we invoke
the formatDate
function to get it to run and insert the date onto the page. In less
formal circumstances, we say that we call
the function. (This is
like call a friend for help
.)
You've been doing this all along, calling or invoking the built-in functions. Here are some examples:
var name = prompt("what's your name?"); // invoke prompt var now = new Date(); // invoke Date formatDate(); // invoke formatDate
Invoking our formatDate
function is no different from
invoking the built-in functions. True, those built-in functions have
arguments, which makes the invocation look a little different,
so we'll turn to that in a moment.
Your code can define lots of functions, maybe in several external .js files so that the definitions can be shared by many files in your website. None of those functions need ever be invoked, in which case they will be useless, like those recipes and kitchen gadgets you never use and which just collect dust. It's important to keep in mind the difference between definition and invocation of a function.
The function above was fairly inflexible: it only formatted today's
date and it always inserted the date into the date2
element on the page. Imagine a kitchen gadget that could chop up ice
but nothing else. If you want to chop carrots, you have to buy a
different machine. To chop celery, yet another machine. A more flexible
and general function would allow you to input the date to be formatted,
and the ID of the element you want to insert the date into. For that,
we have to use a definition syntax that
specifies parameters. We will also have to change
the invocation of the function to supply these
associated arguments.
Here's our modified function. Note that I've changed the name
to formatDate2
only so that it's clear what version of the
function we are discussing. I could have used the same name as the
first version, or I could have named it something different,
like formatDate_version_2
or even fred
.
function formatDate2(dateObj, targetId) { // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; // Add 1 because Jan is 0, etc. var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById(targetId).innerHTML = current_date; }
Now, to get the same effect as we had before, namely to format the
current date into date2
, we would invoke the function like
this:
formatDate2( new Date(), "date2" );
So far, this seems less powerful than what we had before. In fact, let's compare them side by side:
Definition |
function formatDate() { var dateObj = new Date(); // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById("date2").innerHTML = current_date; } |
function formatDate2(dateObj, targetId) { // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById(targetId).innerHTML = current_date; } |
---|---|
Original Invocation | |
formatDate(); |
formatDate2(new Date(), "date2"); |
Additional Invocation | |
??? |
formatDate2(new Date("10/31/2016"), "halloween"); |
If you look at the original invocation, it looks like some of the code has just migrated out of the function definition and become part of the invocation, so it's now harder to use the function. We've transferred work from the implementation to the caller.
This is true. But this burden is useful, because it allows us to use the function in other situations, such as formatting the date of Halloween in another place on the page. You can see that that's impossible with the original version.
Notice, by the way, in that side-by-side comparison, that we are able to make the function more flexible by replacing particular parts of the definition with variables and then those variables are placed between the parentheses in the first line of the function definition:
variable | specific |
---|---|
dateObj |
new Date() |
targetId |
date2 |
This is a fairly common trick: making something more flexible by replacing constants with variables, and supplying values for those variables when the function is invoked.
The names in the parentheses in the function definition are called parameters, and the values that are supplied when the function is invoked are called arguments. (This terminology is not standardize in computer science, but it's common and we will use it.) The parameter is a kind of variable, in that it's a storage location; it stores the corresponding argument.
In the examples above, we had:
Parameter | Argument |
---|---|
Original Invocation | |
dateObj
|
new Date()
|
targetId
|
"date2"
|
Additional Invocation | |
dateObj
|
new Date("10/31/2016")
|
targetId
|
"halloween"
|
If you like, you can think of the beginning of the function invocation as a sequence of assignment statements, assigning the value of the each argument, one at a time, to the corresponding parameter.
If arguments are like inputs to a machine, can we also have outputs from our machines? Yes, a function in JavaScript can have a return value. One slight restriction is that a JavaScript function can have any number of distinct inputs (arguments), but it can only have one return value, just like you can put any number of ingredients into your blender, but you only get one output.
Let's define another function. This one will map day numbers (0 through
6) to the corresponding English name. If we assume that the day number
is in a variable called dayNum
and we want to compute the
result in a variable called dayName
, we might write code
like this:
var dayName = "unknown"; if( dayNum == 0 ) { dayName = "Sunday"; } else if ( dayNum == 1 ) { dayName = "Monday"; } else if ( dayNum == 2 ) { dayName = "Tuesday"; } else if ( dayNum == 3 ) { dayName = "Wednesday"; } else if ( dayNum == 4 ) { dayName = "Thursday"; } else if ( dayNum == 5 ) { dayName = "Friday"; } else if ( dayNum == 6 ) { dayName = "Saturday"; }
That code just uses our skills in cascading if
statements. Now,
we need to package that code up into a function, a little machine that
will take in a number and give us back a string. By take in
, we
mean the function takes an input in the form of an argument. Here's what
we might write:
function dayNameEnglish(dayNum) { var dayName = "unknown"; if( dayNum == 0 ) { dayName = "Sunday"; } else if ( dayNum == 1 ) { dayName = "Monday"; } else if ( dayNum == 2 ) { dayName = "Tuesday"; } else if ( dayNum == 3 ) { dayName = "Wednesday"; } else if ( dayNum == 4 ) { dayName = "Thursday"; } else if ( dayNum == 5 ) { dayName = "Friday"; } else if ( dayNum == 6 ) { dayName = "Saturday"; } return dayName; }
Here's an example of the function in action (that is, an invocation of the function):
var name = dayNameEnglish(0); // should be "Sunday" alert(name);
Again, we refer to the 0 in the invocation as
the argument. Inside the machine, the
parameter dayNum
will have the value 0.
At the end of the function is something new: return
dayName
. That code says that the value of the function is
whatever is in that variable.
Armed with this new function, we can re-write our date formatting
function as follows, now named formatDate3
function formatDate3(dateObj, targetId) { // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; // Add 1 because Jan is 0, etc. var the_year = dateObj.getFullYear(); var dayName = dayNameEnglish( dateObj.getDay() ); // e.g. "Friday" var current_date = dayName + ", "+ the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById(targetId).innerHTML = current_date; }
So that you can test this, we've created the following element:
Note how the formatDate3
function invokes
the dayNameEnglish
function:
var dayString = dayNameEnglish( dateObj.getDay() ); // e.g. "Friday" var current_date = dayString + ", "+ the_month + "/" + the_date + "/" + the_year;
This code is very abstract! The dateObj
variable contains
an object that somebody wants formatted (whoever invoked
the formatDate3
function). We use a
method, .getDay()
to extract the number for the day of the
week. That number is then sent as an argument to
the dayNameEnglish()
function. That function runs and
returns a string, which we store in dayString
and use on
the next line.
When we are programming and defining functions, we often are in a situation where we don't know the particulars. We are trying to create a general solution. We don't know what date is being formatted or where it's going to go on the page. We have to trust that the caller knows what they are doing. In a way, this is like the chef in the kitchen who gets an order for a chocolate cream pie. The chef doesn't know whether the pie will be for dessert or used as a weapon, but that's not their concern. Their job is just to make the pie.
You probably noticed in that last example that we had two functions that we defined, one of which invoked the other. That often happens, and you might think that the computer could get confused about what line of code should be executed when. But, it's actually pretty intuitive.
formatDate3
starts at line 1
dayNameEnglish
, the
computer puts this function on hold and
dayNameEnglish
starts at its line 1
formatDate3
picks up where it left off
This can be nested as deeply as you like (theoretically infinite,
albeit not infinitely deep in practice) and the computer never gets
confused, even when different functions are
invoking dayNameEnglish
. It always knows who and where to
return to.
Throughout our examples, we've never been reluctant to create new
variables inside our functions when we wanted them, such
as the_date
and the_month
. This is perfectly
legal and desirable. These are called local variables, because
they only exist inside the function. They are created when the function
is invoked, and they cease to exist when the function is finished.
If you had very sharp eyes, you might have noticed that our two
functions had a local variable that had the same name,
namely dayName
. That is also legal.
To explain what's going on, I appeal to our third metaphor of functions, as separate work spaces, like an enclosed kitchen. Kitchens have lots of storage containers that are not inputs or outputs --- things like mixing bowls and measuring cups. Different kitchens have different storage containers, even if they are called the same thing. Here's an abstract example:
function fred() { var snackbox = "puking pastille"; alert(snackbox); ... } function george() { var snackbox = "nosebleed nougat"; alert(snackbox); ... }
Here we have two functions named after Weasley twins in Harry
Potter. Each function has a local variable called snackbox
,
but these are completely different storage locations. Think of them as
being in different kitchens, each presided over by a different Weasley
twin. The local variables come into existence when the function is
invoked and disappear when it finishes. (Easy cleanup!)
Local variables are distinguished from global variables which are shared by everything. The following example may help. Read the code, then run it, and see if you can make sense of the behavior.
You saw the following sequence of output:
The amazing thing is that all three of these variables are separate storage locations, so none of the assignments affects any of the others. There's a "snackbox" in Fred's private area; another, different, one in George's private area, and a third one in the common room that everyone shares.
Usually, that's exactly what you want. Occasionally, a function wants
to modify the global variable rather than a local variable. To do so,
just omit the var
before the variable name:
This time, you saw the following sequence of output:
The best practice is always to use local variables, creating them
with var
, and only to use global variables when necessary.
A bit of terminology: the places that a variable is visible or usable is called its scope. We might say that some variables have scope that is local while others are global.
Earlier, we noticed that the improved version of date formatting,
namely formatDate2
, resulted in more work for the caller:
Original Invocation | |
---|---|
formatDate(); |
formatDate2(new Date(), "date2"); |
Additional Invocation | |
??? |
formatDate2(new Date("10/31/2016"), "halloween"); |
In both of these, the first argument is an expression that involves
the Date
function and so on. As programmers, we can make
the caller's job easier with just a bit of work. Here's what we would
like to say instead:
Original Invocation | |
---|---|
formatDate(); |
formatDate2b("today", "date2"); |
Additional Invocation | |
??? |
formatDate2b("10/31/2016", "halloween"); |
That's much clearer and easier, and just involves a little bit of work
for the implementation. Here's one way, with the conversion of
the dateSpec
parameter (date specification) into the
desired kind of Date object. This happens in the lines 2--6 of the
function, and everything else is the same.
function formatDate2b(dateSpec, targetId) { if( dateSpec == "today" ) { var dateObj = new Date(); } else { var dateObj = new Date(dateSpec); } // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; var the_year = dateObj.getFullYear(); var current_date = the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById(targetId).innerHTML = current_date; }
The point here is that these interfaces between functions and other bits of software are designed by humans, and they can be easier and more convenient if we want them to be. This is part of creating an Application Programmer Interface (API). An API is a term for a set of software interfaces, such as functions and their arguments, that allow an application programmer to get something done.
At this point, you know most of what you need to know about functions
for this semester, but one mind-blowing concept remains. Programming
languages describe certain values as being first class
objects
when you can do normal things with them like store them in variables and
pass them as arguments to a function. In JavaScript, as you know,
numbers are first-class objects, and so are strings and even booleans.
Date objects are first-class.
The mind-blowing thing is that functions are first-class objects! That means we can store them in variables and pass them as arguments to a function. For example, we can do the following:
var dayNamer = dayNameEnglish;
If the function is stored in a variable, can we still invoke it? Yes!
It turns out that the parentheses in a function invocation are sort-of
like the on
button of a machine. Here's how we could invoke that
function above:
dayNamer( 0 ); // Sunday
So what? Imagine we define another day naming function:
function dayNameFrench(dayNum) { var dayName = "unknown"; if( dayNum == 0 ) { dayName = "Dimanche"; } else if ( dayNum == 1 ) { dayName = "Lundi"; } else if ( dayNum == 2 ) { dayName = "Mardi"; } else if ( dayNum == 3 ) { dayName = "Mecredie"; } else if ( dayNum == 4 ) { dayName = "Jeudi"; } else if ( dayNum == 5 ) { dayName = "Vendredi"; } else if ( dayNum == 6 ) { dayName = "Samedi"; } return dayName; }
Our date-formatting function seems to be always using the English days
of the week. Could it be made even more flexible, where we supply a
function that returns the correct day name? Yes, we can. Here's the
latest version, now named formatDate4
:
function formatDate4(dateObj, dayNamer, targetId) { // format info about the day var the_date = dateObj.getDate(); var the_month = dateObj.getMonth() + 1; // Add 1 because Jan is 0, etc. var the_year = dateObj.getFullYear(); var dayName = dayNamer( dateObj.getDay() ); // e.g. "Friday" or "Vendredi" var current_date = dayName + ", "+ the_month + "/" + the_date + "/" + the_year; // insert the formatted string into the document. document.getElementById(targetId).innerHTML = current_date; }
So, the dayNameEnglish
function on line 6 has now become
a variable, a parameter whose value will be supplied
when the function is invoked.
So that you can test this, we've created the following element:
Here's the key thing to notice and remember. When we
have dayNameFrench
in the invocation, we don't use
parentheses. That's because we are not invoking it right now, but we are
passing it (the dayNameFrench
function) into
the formatDate4
function. This is like handing someone a
blender or other kitchen gadget instead of the output of the gadget.
This may seem like an esoteric usage, and it is. We won't be asking you
to define functions as sophisticated
as formatDate4
. However, we will expect you to understand
the difference between invoking a function, where the parentheses are
used, and passing it as an argument to another function, where the
parentheses are omitted.
We covered a lot of difficult ground in this reading. The main points are:
This reading introduces the Document Object Model, or DOM, and a widely-used JavaScript library called jQuery. jQuery is used in thousands of websites and is supported by Google, IBM and other tech companies because it makes working with the DOM much easier and more reliable.
Earlier in this course, we use HTML to structure our web pages and supply them content. We used CSS to give them some style. Then we introduced JavaScript, but the JavaScript barely seems to have anything to do with the HTML and CSS, other than running in the web browser.
There's one exception, and that is code like the following:
document.querySelector("#fred").innerHTML = "I'm Fred";
The preceding JS code will actually modify the web page, changing the
contents of the #fred
element (the element whose id
is fred
), to contain the string "I'm Fred".
The first part of the JS expression above, namely:
document.querySelector("#fred")
returns an object that represents something in the web
page. That thing could be a paragraph, a header, a div, or any
other element. The object has properties, such
as innerHTML
, and modifying that property modifies the web
page. Thus, the web page is dynamic.
To understand this better, let's review what we know about web pages.
The Document Object Model (DOM) is the application programming interface (API) to represent and interact with an HTML document.
The DOM represents the HTML document as a tree of nodes. Every node represents a portion of the document. Explore below an example of how a simple HTML file is represented by its DOM.
<!doctype html> <html lang="en"> <head> <title>My blog</title> <meta charset="utf-8"> <script src="blog.js"></script> </head> <body> <h1>My blog</h1> <div id="entry1"> <h2>Great day bird watching</h2> <p> Today I saw three ducks! I named them Huey, Louie, and Dewey. </p> <p> I took a couple of photos ... </p> </div> </body> </html>
The DOM represents both the structure and the content of the HTML
page. All elements of the structure (i.e., tags) are represented by
nodes surrounded with a border in the tree. In the Javascript
representation they are all objects. The content of
these objects is shown in the graphical representation without a
border, and it's accessed by properties (such
as innerHTML
) of the objects that contain them.
The DOM is a bridge between the HTML world of elements (paragraphs, lists, divs, and so on) and the JavaScript world of variables, functions, objects, and methods. Every web browser has the DOM built-in, and the code we saw earlier is part of it:
document.querySelector().innerHTML
However, we will not be using the built-in DOM. Instead, we will be using an add-on library called jQuery. We prefer jQuery for several reasons:
Let's start with an example of jQuery's conciseness. The following two lines of code are equivalent:
document.querySelector("#fred").innerHTML = "I'm Fred"; $("#fred").html("I'm Fred");
The latter, of course, is jQuery, the topic that we turn to now. If you want to learn a bit more about the DOM, the following answers the question What is the DOM? in a brief page consisting of four big pictures and a small amount of text.
jQuery is a library written in Javascript that is specialized for changing web documents on the fly. It is one of the most used libraries on the web. However, because it's not built-in to the browser, it has to be loaded. If you don't load jQuery, none of these examples will work.
In order to use jQuery in a webpage, we need to include it with
the <script>
tag. The library itself is a big
Javascript file with hundreds of lines of code. We can either download
this file from the jQuery
website, or we can use an absolute URL to some web server that keeps
a copy of the library online. For simplicity, we will do the
latter. Here is how we will include jQuery in our programs:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
As of this lecture, the most current version of jQuery is 2.2.1, but
1.11.0 will do. (The main difference between the 2.x and 1.x versions is
whether older browsers like IE 6/7/8 are supported.) In your reading or
examples from the Web, you will notice other version
numbers. The .min
part of the filename
means mininfied. This refers to the practice of deleting all
newlines and other space to reduce file size. If you open this file,
you'll notice that it doesn't show the structure of a normal Javascript
program with indentation and nesting. Thus, it is not for human
consumption. However, it does load faster, because the file is
smaller.
Once we have the jQuery library linked to our page, we can start using its main method, the jQuery Wrapper. This is a function that has two forms:
$() // Short form is common jQuery() // Long form is rare
In practice, the dollar sign is used almost always. The long form is only used in a few odd cases when your page is loading several libraries, both of which use the dollar sign. In this course, you will always use the dollar sign.
As we said earlier, jQuery is a bridge to the web page, so most of what we will be using it for is to dynamically modify the web page, or to attach behaviors to it. Here are some of the things we might want to do:
We'll see most of those today. In later readings, we'll see even more that jQuery can do.
Code using jQuery has a very nice, consistent, concise syntax, so let's take a minute to learn that syntax using an example we already understand, namely modifying the HTML in an element.
Our example uses a element created by the following HTML/CSS code that
creates an empty, green-bordered box on the page, whose ID
is pandora
.
<div id="pandora">This is Pandora's box</div> <style> #pandora { width: 80%; margin: 0 auto; border: 2px solid green; padding: 5px; } <style>
Suppose we wanted to put some text dynamically into Pandora's box (replacing what is there). The following lines of JavaScript/jQuery do the trick.
Try putting hope
into the box instead.
Let's try to understand this example of jQuery code. (Remember, jQuery is an extension of JavaScript, not a whole different language, but it can seem like a different language at times.) Here it is again:
var contents = prompt("What to put in Pandora's box?", "all evils"); console.log("putting "+contents+" into Pandora's box"); $('#pandora').html(contents);
The first two lines of code are things we have seen
before: prompt()
gets a string from the user and the first
statement stores that string in a variable
called contents
. The second line glues a few strings together
(some of which are string literals and one of which is in
the contents
variable) and then writes the combined string
onto the console log.
The third line puts the string into the box, using a jQuery incantation:
$('#pandora').html(contents);
How does the incantation work? There are basically three things going on here:
$(selector).method(stuff);
pandora
. Hence it uses #pandora
, exactly
like the CSS does. Thus, all your skills with selecting elements using
CSS will serve you well when using jQuery.
.html()
method replaces the contents of all the elements that were chosen by the
selector (in this case, just one).
So, in summary, we used jQuery to find the div and insert the user's input into it. What we haven't shown you is how to trigger this behavior in a more elegant way than one of these execution boxes. That will have to wait for a later lecture. Be patient. For now, though, we'll demonstrate some of the abilities that JQ has to dynamically alter a web page. We won't go into great detail in any of them; we just want to give you the basic idea.
As we've seen, the html
method modifies the contents of
the page. It doesn't have to take just text; in general, it can take any
HTML. (To learn all there is about it, read the documentation for
the html method.) Here's
another example, using a list.
<ol id="horcruxes"><li>currently empty</li></ol>
Let's replace that with some other HTML
jQuery has several ways to modify the attributes of an existing elements. Let's start with changing the CSS of a figure:
<figure id="fig1"> <img id="img1" src="potterpics/harry-potter-thumb.jpeg" alt="Harry Potter"> <figcaption id="cap1">Harry Potter as played by Daniel Radcliffe</figcaption> </figure>
We could change the CSS by adding a border, centering, a background-color and some padding. Click the button to implement these changes. Notice the descendant selectors we use for the caption.
The .css()
method takes two arguments, a property and a
value, just like static CSS. It works exactly like inline CSS. Try
inspecting the figure and figcaption elements above; you'll see that they
have had inline CSS attributes added to them. (However, this is not a bad
use of inline CSS, because the CSS is not mixed with the static HTML code;
it's being dynamically specified.)
There are other attributes we could change, such as the SRC and ALT for the image here:
<figure id="fig2"> <img src="potterpics/hermione-granger-thumb.jpeg" alt="Hermione Granger"> <figcaption>Hermione Granger as played by Emma Watson</figcaption> </figure>
The attributes above can be changed using jQuery's attr()
method. Watch what we can do with it; it's like magic!
If you want to make something disappear, you could modify its CSS to
have the property display:none
. You could undo that change to
make it re-appear. This is so common that jQuery has special
methods, .hide()
and show()
. Here's our figure
again, this time as fig3
:
We could describe more methods, but hopefully you are seeing the pattern: select an element and operate on it using a method. So, let's turn to some slightly different tricks jQuery has up its sleeve.
So far, we've always seen the selector part of the jQuery expression pick out just a single element. However, jQuery is more general and powerful than that. In most cases, if the selector matches more than one element on the page, jQuery operates on all of the elements in one fell swoop. Here's an example:
<ol id="characters1"> <li class="gryffindor">Harry</li> <li class="gryffindor">Ron</li> <li class="gryffindor">Hermione</li> <li class="gryffindor">Fred</li> <li class="gryffindor">George</li> <li class="ravenclaw">Cho</li> <li class="hufflepuff">Cedric</li> <li class="slytherin">Draco</li> <li class="slytherin">Crabbe</li> <li class="slytherin">Goyle</li> </ol>
The jQuery wrapper function always returns the set of elements it matches, so you can operate on it again, if you like. Knowing that the jQuery wrapper always returns something is very useful for chaining jQuery methods. This is done to avoid storing results in temporary variables, or repeating a search for set of matches. So, some of our examples above could be even more concise:
We could change the CSS by adding a border, centering, a background-color and some padding. Click the button to implement these changes.
This is not only less to type, it's easier to understand and it's faster to execute, because it now finds the element once and does a series of operations on it.
Note, though, that we also omit the semi-colon on all the lines except the last. The semi-colon breaks the chain of method calls.
Let's turn to a more advanced case of chaining, along with some other cool tricks that jQuery can do, namely
off stage.
Let's see it in action on this list, repeated from above, but now with
the ID characters2
The code above creates a new empty list item, sets its contents to
Percy, adds the Gryffindor class to give it the correct styling, adds some
addition CSS to strike him out (since he's such a git), and finally
attaches it to the page at the end of the element whose ID
is characters2
(our list).
You don't have to use the chaining technique if you don't want to, but it can be very clear to read (once you are used to it) and it is commonly used in online tutorials and examples. We will use it whenever it's convenient.
Common practice for many years has been to put JS code in the HEAD of a web page, but that lays a trap for us. If we write some JQ code to find an element and do something to it, as we've done many times in this reading, but the HTML for that element is further down on the page, the element will not exist when the JQ code runs. Here's an example:
<head> <!-- load jQuery --> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script> var d = new Date(); $("h1").html("News for "+d.toLocaleDateString()); // insert date into H1 </script> </head> <body> <h1>News for Today</h1> <!-- replaced by today's date --> ...
Our intention is pretty clear: find the H1 and insert today's date in it. However, when the JavaScript/jQuery code runs, the H1 does not yet exist, so it won't get filled in with today's date.
Even worse, we won't get an error message! jQuery successfully matched a set of elements; it just happened to be the empty set, which is fine by jQuery, if not for us.
The pitfall we just described leads to the important question of when our jQuery code should be executed. Putting the code in different parts of the HTML file might have different effects on the page, based on when the browser reads and interprets the code. This is why, normally, the jQuery code should be executed only after the DOM has been created and is ready for dynamic manipulation.
Most examples you'll see in the provided reading, will show code
usually wrapped in a big event handler for the document
object, as shown below:
$(document).ready(function(){ // all other jQuery code goes here });
For example, the date insertion we did above would be done like this:
<head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script> $(document).ready(function () { var d = new Date(); $("h1").html("News for "+d.toLocaleDateString()); }); </script> </head> <body> <h1>News for Today</h1> ...
This works correctly, but is complex to understand. It uses the jQuery
wrapper function with the special document
object and the
result gets the ready()
method is triggered when the DOM is
ready), and to this method it passes as an argument an anonymous function.
It's also a syntactic nightmare: notice that the final line
is });
.
Another way to make sure that the DOM is ready is to put the JavaScript
code at the very end of the page (right before </body>
instead of in the head. This way, we know that the previous HTML lines
have been already processed by the browser and the DOM is ready. In other
words, the following code would also work for putting today's date in the
H1 element, and is simpler and easier to understand:
<head> </head> <body> <h1>News for Today</h1> ... <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script> var d = new Date(); $("h1").html("News for "+d.toLocaleDateString()); </script> </body>
In this class, we'll load jQuery and put our code at the end of the file, when the DOM has been built and is ready.
$(document).ready(function(){..});
We taught you about $(document).ready()
not because we
will use it in this course, but because it is extremely common in
examples and tutorials about jQuery on the web. We want you to be able
to understand those.
We'll learn more about jQuery this semester. If you can't wait, there are many good tutorials online. One place to start is W3Schools jQuery Tutorial.
jQuery can help us with a common pitfall in larger websites: the problem of copied code. For example, suppose every web page of a 10-page website (like your CS 110 project sites) has the same navigation bar. So, all 10 pages have the same HTML, something like the following (assume there's also some nice CSS to lay out the links and make them look pretty).
The obvious thing to do is to copy/paste those lines to each of the 10 pages of your site. That works fine, but if the boss decides to change the nav content (say, adding another link), you'll have to edit all 10 pages of the site. Note that if the boss just wanted to change the CSS, you'd be able to just change the shared CSS file. Having things just written once is a big advantage.
Using jQuery, we can mitigate this problem. We can put the master copy of the nav on the home page, and use jQuery to dynamically copy that content to the other pages. The other pages have an empty element where the nav will be copied to:
<div id="nav-goes-here"></div>
Then, they have some jQuery code that looks like this:
$("#nav-goes-here").load("home.html nav");
That incantation dynamically loads home.html
into memory,
pulls out the <nav>
element and inserts it into the
element whose ID is #nav-goes-here
.
You can stop here if you like. The following is for the interested reader.
Suppose we had some class definitions that we wanted to use:
.gryffindor { border: 2px solid red; background-color: gold; } .slytherin { border: 2px solid green; background-color: silver; } .hufflepuff { border: 2px solid black; background-color: yellow; } .ravenclaw { border: 2px solid blue; background-color: bronze; }
Here's the source code for the figure above:
<figure id="fig2a"> <img src="potterpics/hermione-granger-thumb.jpeg" alt="Hermione Granger"> <figcaption>Hermione Granger as played by Emma Watson</figcaption> </figure>
Click the button to change the figure to use Slytherin's colors. Try changing the code and clicking again to change to Gryffindor's colors.
TBD
TBD
Here is a list of jQuery methods used in our interactive example or in the assignment for this week.
To see more examples and explanations for these methods, you should consult the jQuery API documentation.
This reading is entirely about HTML, but forms are the primary way of getting input from the user, and we will then turn to how that input can be accessed using the DOM and jQuery and then processed by our JavaScript code. To add the form processing functionality, we'll have to learn about events and event handlers, which is in the next reading. This reading is just HTML.
We will not be covering all aspects of HTML forms. We'll focus on just a handful that are useful in this course. If you want to learn more, there's some additional material at the end, and you're welcome to talk to one of us. The most important kinds of inputs we'll learn are:
form
tagHTML has a dedicated tag, form
that is used as a container
to package data that might be sent to a web server. The
attribute method
of the element allows the form to either
request data from the server (method="GET"
), or send data
to the server (method="POST"
). However, in order for both
these operations to happen, the server needs to have some dedicated
programs (also known as scripts) that can deal with the form data. At
this point, we will not talk about how this happens (we'll postpone this
discussion for later in the semester), and only concentrate on the HTML
elements that are contained inside the form.
Let's see an example form, and then we'll look at the code that creates it.
If you see the above form outside an HTML page with CSS, it will look different. Our lecture page applies styling to it that is not the default way a browser will display a form. Please see how the unstyled form looks like.
Additionally, keep in mind that all form elements are inline
elements,
therefore, in order to appear in separate lines, we wrap them in p
elements.
Here's the code that creates that form:
As you can see, there's an outer FORM
element that wraps
up the form inputs. There are input elements that correspond to
different places where the user can enter information. Most of the
inputs use the INPUT
tag, but some use tags
like SELECT
(for a drop-down menu)
and TEXTAREA
(for larger blocks of text). The general term
is control. Finally, there's a BUTTON
input at
the end. In a more complete example; clicking this button would send the
form data to the web server; this one doesn't do anything.
Let's look at the different input elements. The following table shows
the HTML syntax for including different HTML elements in a form. As you
will notice, the most common element is <input>
,
which, based on the value for its attribute type
will
display a different kind of input. Play with the rendered version of a
tag in every row in the table.
For more information on the form
elements and all its fields, consult
the W3Schools page on forms.
Let's look at some of the more important controls, and then other aspect of
The input
tag is fairly straightforward, but you can
specify the type of input you are looking for, using the TYPE
attribute. It has many more types, which we are not listing here;
consult W3Schools
page for input to see the complete list. Here are just a few:
TEXT
: allows the user to type in a word
or phrase
PASSWORD
: allows the user to type in a word or
phrase, but its value isn't echoed, so no one can look
over their shoulder and see it.
email
: like a text type, but should look like an
email address. New with HTML5.
date
: for entering dates. New with HTML5.
time
: for entering times. New with HTML5.
Some of these types (such as time, date, number, range, etc.) were
introduced in HTML5, which means that not all browser versions are able to
support them. For maximum portability, you should stick
to type=text
. However, sliders like we get
with type=range
are fun, and we'll use them in this course.
To specify a menu, from which the user can choose only one option,
you use the SELECT
tag inside the form. You specify the NAME
of the input in
the SELECT
tag, and each menu item is specified using the
OPTION
tag. Here's an example:
<form action=""> <p>Drink: <select name="beverage"> <option value="">choose one <option value="Coke">Coca-Cola</option> <option value="Pepsi">Pepsi-Cola</option> <option>Beer</option> <option>Wine</option> </select> </form>
(The closing </option>
tag is optional, like a closing
</p>
tag or </li>
tag, but it's best
to use it.) Any option can have a separate "value" attribute; if none is
specified, the value is the option itself.
Specifying a non-option
as the first item in the list helps to
tell whether someone has actually made a choice or just overlooked this
menu. Making the non-option have a value of the empty string helps with
validating the form.
If you want to allow your user to type in a long response, you should
define
a textarea
inside your form. This tag has attributes called ROWS
and
COLS
that let you specify the size of the area.
<textarea name="thoughts" rows="3" cols="40"> A chicken is an egg's way of making another egg </textarea>
The default value is the region between the beginning and ending tag. Typically, you want the default value to be empty, so put the tags right next to each other, as in this example here:
<textarea name="thoughts" rows="3" cols="40"></textarea>
Don't let even a single space creep in, or the initial value will be a string of one space, and not the empty string. That will affect any code that cares about the default or original value, such as certain kinds of validation
A form consisting of a bunch of bare boxes would be useless, so how is
the user to know what input box means what? That's done with
the label
tag. There are two ways the label can be
used. One option is to wrap the input in the label:
<label> Given Name <input type="text" name="givenname"> </label> <label> Family Name <input type="text" name="familyname"> </label>
The other is to give the input an ID and reference that ID in
a for
attribute of the label:
<label for="givenname">Given Name</label> <input type="text" name="givenname" id="givenname"> <label for="familyname">Family Name</label> <input type="text" name="familyname" id="familyname">
The latter is a bit more work, but is necessary in some cases where the structure of the HTML doesn't allow the input to be a child of the label, as with a table.
When the form gets submitted, the form data gets sent to the server. It gets sent as a set of name/value pairs, which we can think of as like a little table. Here's some data as if from our pizza form:
name | value |
---|---|
customer | Hermione |
phone | 555-8888 |
addr | hgranger@hogwarts.ac.uk |
size | large |
due | 21:00 |
instructions | please deliver by owl |
Since the fields of a form need to be processed both by Javascript code on the client-side (by the browser) and the scripts on the web server, it is necessary to use the different attributes of these elements to distinguish and access them.
Consequently, the two most important attributes that we will use very
frequently are name
and value
.
name
attribute is used by Javascript to reference the HTML elements
that use it, but most importantly is used by the server to distinguish between the different
fields of the submitted form. We will discuss it again later when we talk about submitting
the form to the server. In the meantime, it will be good practice to start using it everytime
we create form fields.
value
attribute is an attribute that can be
either set explicitly in the HTML code to give an initial value to
the elements, or will be set implicitly by the browser when the user
enters a value in the field or selects some option (as from a
menu). For example,
<input type="text" value="Wellesley">
will show
an initial value in the text field: , that can be overwritten by the user.To control the width of the text input field, you don't use the attribute width, but the
attribute size
. For example, <input value="Wellesley" size="10">
will now be much smaller than in the example above: .
Another useful attribute for input
controls is
placeholder
, which can be used to provide a hint for the
kind of value that should go in a field. For example, this
HTML <input type="text" placeholder="Enter your
name">
will be rendered like this: .
Later in the course, we'll talk about submitting the form data to a server for more elaborate processing, but before we do that, we should discuss validation. What is form validation? Essentially, it means checking to see that the form has been filled out correctly (as far as we can tell).
Form validation could be used to ensure that someone hasn't overlooked a text input, menu or radio button group, and can check that, for example, the zip code is 5 digits (or 9) and that a telephone number is 10 digits, and that an email address looks like an email address.
Form validation can actually cancel the submission of the form, so that the data never leaves browser. The reason we validate forms is twofold: to give the user immediate feedback that they've missed something, instead of waiting for the server to respond, and to protect the server from having to handle all those invalid forms. Of course, a determined nefarious person can simply disable our form validation JavaScript and hammer our server with invalid forms, but that's rare. The vast majority of invalid forms are just human error.
Obviously, the browser can't tell whether you entered your correct
phone number, but it can check that you typed the right number of digits
(and only digits). Similarly, it can't check that your spelled your name
correctly (and whether your name really is Mickey Mouse
), but it
can check that you didn't leave that input blank.
With HTML5 and modern web browsers, form validation has gotten a lot easier. In the past, web developers would write JavaScript code that would look at the values in the form to check for bogus values. They wrote libraries and jQuery plug-ins to make the job easier for others. Indeed, there is a jQuery plug-in by Jörn Zaefferer called validation that does a good job and is well described in our book, JavaScript & jQuery: The Missing Manual, pages 278-300. Please read that, and the on-line documentation linked above, if you're curious.
However, the vast majority of form validation can be done with a few simple things:
required
to any input that you want
to require the user to fill out.
Here's a demonstration. (Note that I didn't re-use the input names from the earlier example because the JavaScript code above referred to the inputs by name, and so we need to make those unique. I could also have used a nested selector and given each form its own ID.)
<form action="/cgi-bin/dump.cgi"> <p>Username: <input required name="username"> <select required name="hogwarts_house"> <option value="">Hogwarts House</option> <option>Gryffindor</option> <option>Hufflepuff</option> <option>Ravenclaw</option> <option>Slytherin</option> </select> <p>Email address: <input required name="email" type="email"> <p>Birthday: <input required name="birthday" type="date"> <p><input type="submit" value="submit form"> </form>
Here's the actual form, so you can change the values of inputs:
Try to submit an incomplete form!
You can stop here. The following information is just for those interested in learning more.
TBD
Our websites have so far been pretty passive: they present material for the user to read or look at, but nothing more interactive. Now that we know about forms, our code can process input from the user and dynamically modify the page. We just need a few more concepts to make a page that is interactive:
Let's start with the last item.
jQuery has a convenient method for retrieving the value of an input,
the .val()
method. The selector should be anything that
selects the input, such as its id. Here's a form with three inputs:
The IDs of the three inputs
are customer1
, pizza_kind1
,
and num_slices1
.
Fill out the form and use the following execution box to retrieve the values:
The .val()
method works just like other jQuery methods:
the wrapper function selects the element you want to operate on, and the
method does the work. Here, it pulls the value
out of the
input.
Note that values from forms are always strings, even if the
form says that the input is type number
. If you want it to
be a number, you have to use parseInt
or parseFloat
, just as we did with values returned
by prompt
.
Now that we know how to use forms, we'll never need prompt
again.
Let's build a slightly useful form: one that will allow the user to do metric conversions. They will enter a number of pounds, and the form will compute the conversion to kilograms. (One kilogram is 2.2 pounds.)
We'll define two functions to do the work:
pounds2kilograms
, which takes an argument of pounds
(a number) and converts it to kilograms, returning the number of
kilograms. This is a generic function that might be useful in a
wide variety of problems.
doConversion
, which pulls the numberof pounds out of
the form, converts it to a number, invokes the first function to get
the number of kilograms, and then inserts the answer, formatted with
1 decimal place, into the span above.
Here's the source code for the form we will use:
<form> <p><label for="pounds1">Pounds</label> <input type="text" name="pounds" id="pounds1"> <p><button type="button" id="convert1">Convert</button> <p><span id="kg1"></span> kilograms</p> </form>
Here's the live form. The button doesn't do anything yet.
Here's the code to execute:
At this point, we almost have a working form. What we need is
to set things up so that instead of the execution box invoking
the doConversion
function, the user can cause it be be
invoked by clicking on the button.
To re-phrase that in the terminology of this reading, we want
the doConversion
function to be an event handler
for the event of the user clicking on the button.
jQuery has a convenient method of attaching a function to some element
in the document so that the function gets invoked when the element is
clicked on. That method is the .click()
method. Here's how
we will do it:
$("#convert1").click(doConversion);
Notice, by the way, the lack of a pair of empty parentheses after the
function name, doConversion
. That's because we are not
invoking the function here. Instead, we are handing it to jQuery to be
attached to the web page as an event handler. To use our earlier
metaphor, we are passing the function as a machine, like a blender or
meat grinder, for someone else to use, but we are not pressing
the on
button right here.
Here's our revised code:
Let's see the code in action. Here's a duplicate form, but now the IDs end in 2 instead of 1:
Go ahead and try the form!
The preceding section showed a particular example of a event
handler (the doConversion
function). It will be
invoked to handle a "click" event on that particular button. Let's
step back and discuss more generally what these things are.
An event is something that happens in the browser, often initiated by the user, but not always. Some examples:
There are others, but these are more than enough for our purposes.
An event handler is just a function that will be invoked when a particular event occurs. In other words, it's not a special kind of function, it's a particular use for a function. That said, for our purposes, an event handler will be a function that takes no arguments and returns no values, because that's how it will be invoked. (That's not strictly true, but it is close enough for now.)
Our forms don't have to have lots of inputs; they can consist of just a button or two. Here is a form with just two buttons. Try them, and watch the picture below!
Here's the code. You'll notice that there are two simple functions that
hide/show the element whose ID is fig1
. You're welcome to
try them from the JS console; there's nothing special about them. Then,
we have two lines of JQ that attach those functions to the two buttons.
function hideFigure1() { $("#fig1").hide(); } function showFigure1() { $("#fig1").show(); } $("#hide1").click(hideFigure1); $("#show1").click(showFigure1);
this
ObjectSuppose we had a long list of items, and we want to allow the user to click on any one of them to make them disappear. Here's an example of the behavior we want:
Clearly, we could give each of these 11 items a different ID, we could define 11 functions that are all pretty much the same except for the ID of the thing they are hiding, and attach each function to one list item. Something like this. I've written the functions on one line both for brevity and to make it easier to compare their code.
function hideItem1() { $("#elt1").hide(); } function hideItem2() { $("#elt2").hide(); } function hideItem3() { $("#elt3").hide(); } ... $("#elt1").click(hideItem1); $("#elt2").click(hideItem2); $("#elt3").click(hideItem3); ...
Code like that is tedious and boring, and it doesn't scale well. If we had a list of the 50 states in the US, or 196 countries in the world, the code is excessively tedious and boring. There must be a better way, and there is.
The idea is to attach the same function to each item in the list, so we only have to define one function. The function, however, has to figure out which item to hide. In this case, the answer is the one that was clicked on.
Any time an element is clicked on, the special
variable this
contains the element. So, in the example
above, if you click on the first element in the list (Harry),
the hideItems1
function runs, but this
contains that LI. Similarly for all the others.
Here's our revised code:
function hideItem() { $(this).hide(); } $("#char_list li").click(hideItem);
Try it!
One important part of the code is the selector, "#char_list
li"
, which matches all 11 LI elements in that list in one, easy
but powerful bit of code.
The key piece of code, of course, is the following:
$(this).hide();
That code uses this
as the selector, taking that element
on the page and wrapping it up with all the jQuery methods, including
the .hide()
method. Before, we've always seen the selector
contain a string literal, describing the element or elements to be
operated on. Here, we have, essentially, a magic variable whose value is
just the thing we want.
The technique of using the this
variable is fairly complex
and advanced, and we won't need it often, but it's good to know about.
In this reading, we'll see the DOM and jQuery in action. We'll create an image gallery and then a drop-down menu.
Often, you want to display a set of pictures and allow the user to choose which ones to look at more closely, something like an art gallery. Therefore, we'll call this JavaScript application a gallery. We'll have an arrangement of small pictures, often called thumbnails, and, if the user clicks on any one of them, a larger version is displayed.
Note that one advantage of a gallery is that a set of thumbnails can load quicker than even one of the larger versions, if the file sizes are much smaller. Therefore, you should go to the extra effort to make thumbnail-size images, using an image editor (like PhotoShop or Pixlr or the Gimp), rather than just using the width and height attributes to make the squeeze the big files down to a small screen area.
This will just be a small gallery, with just four pictures. In this version of the gallery application, we'll allocate space on the page for the larger version. In this example, we'll put a boring pale-yellow image there. If you decide to build a gallery application, you could choose one of the large images, either randomly or deterministically, or something else entirely.
Please try this gallery example
An alternative is to have the large image take over the window, putting everything else behind a semi-transparent dim filter, so that the user's attention is necessarily on the large image. This is called a Lightbox, and has been implemented many times since its originator, Lokesh Dhakar. We've implemented a very simple version of it; there are many more sophisticated ones. The lightbox, of course, requires an additional bit of coding to make the image go away so that the user can look at another image or do anything else.
Please try this lightbox gallery
Creating a gallery involves first putting a set of thumbnails on the page. That can easily be done by copy/pasting a bunch of HTML, like this:
<figure data-bigsrc="../potterpics/harry-potter-big.jpeg" data-alt="Harry Potter"> <img src="../potterpics/harry-potter-thumb.jpeg" alt="Harry Potter"> <figcaption>Harry Potter</figcaption> </figure> <figure data-bigsrc="../potterpics/hermione-granger-big.jpeg" data-alt="Hermione Granger"> <img src="../potterpics/hermione-granger-thumb.jpeg" alt="Hermione Granger"> <figcaption>Hermione Granger</figcaption> </figure> ...
That's acceptable, but a bit tedious and error prone. When you copy/paste the code, you have to remember to change all the places where we see "Harry Potter" to "Hermione Granger." If you miss one, rabid fans will notice when the caption for Hermione says Harry.
An alternative is to define a function to create one of these gallery items:
function addToGallery(galleryID,thumbsrc,bigsrc,alt) { console.log("add figure "+thumbsrc); var img = $("<img>") .attr("src",thumbsrc) .attr("alt",alt); var cap = $("<figcaption>"+alt+"</figcaption>"); $("<figure>") .attr("data-bigsrc",bigsrc) .attr("data-alt",alt) .append(img) .append(cap) .appendTo(galleryID); }
You can then invoke it with the necessary info. You'll notice that there's still a lot of repetition, but because only the key info is shown, it's easy to be thorough. (We could also define a higher-level function that could create the proper URLs, if we used a simple naming scheme. We won't describe that here.)
addToGallery("#gallery1", "../potterpics/harry-potter-thumb.jpeg", "../potterpics/harry-potter-big.jpeg", "Harry Potter"); addToGallery("#gallery1", "../potterpics/hermione-granger-thumb.jpeg", "../potterpics/hermione-granger-big.jpeg", "Hermione Granger"); ...
You probably noticed the two attributes starting
with data-
. Here's the code again:
<figure data-bigsrc="../potterpics/harry-potter-big.jpeg" data-alt="Harry Potter"> <img src="../potterpics/harry-potter-thumb.jpeg" alt="Harry Potter"> <figcaption>Harry Potter</figcaption> </figure>
We've learned about lots of tag attributes in HTML, such as SRC for IMG
and HREF for A. Those are all built into the HTML
language. These data-
attributes are something new. They
aren't built into the language, but we (the authors of the web page)
get to make them up. More precisely, the
prefix data-
is built into the language, but we get to
make up the suffix.
When we click on the figure, which we'll turn to in a moment, we'll
need the SRC and ALT for the big version, so we stash the values
here. Is this legal? Yes. The design of HTML5 anticipated the need for
data sprinkled throughout the DOM tree, and explicitly allowed the user
to create any attributes they want, as long as they start
with data-
. So, we're putting that information there so
that it'll be there when the event handler needs it.
We also need a place to display the big version of the picture. (This is the main difference between the simple and lightbox version of the gallery.) For the simple version, we just need a figure:
<figure id="large_image"> <img src="../potterpics/pale-yellow-266x200.png" alt="filler image"> <figcaption>A pale yellow image filler image</figcaption> </figure>
We put the pale yellow box there just so that the IMG has a SRC when the page loads. We should probably put an image from the gallery in there, but this explanation is a little clearer if there's a different image there at the beginning. We would use CSS to make this figure larger.
Finally, all the pieces are in place for the image gallery. We'll
define a function that will be attached to each thumbnail, and it'll
cause the larger version to be displayed in
the figure#large_image
that we just saw. Here's the code:
function enlargeGalleryImage() { var bigsrc = $(this).attr("data-bigsrc"); var alt = $(this).attr("data-alt"); $("#large_image img") .attr("src",bigsrc) .attr("alt",alt); $("#large_image figcaption").html(alt); }
Next, we have to attach it to every thumbnail in the gallery:
$("#gallery1 figure").click(enlargeGalleryImage);
This function is hard to understand because it is abstract:
there's just the one function, but it's used for 4 or 400
thumbnails. For each of those thumbnails, a different enlargement has to
be loaded. Fortunately, we put the URL of the enlargement on the figure
that was clicked on, so we can find out the URL using the magic
variable this
. Here's the key part:
var bigsrc = $(this).attr("data-bigsrc");
Each thumbnail as a data-bigsrc
attribute, and the event
handler can grab that attribute out of the thumbnail. Which thumbnail?
The one that was clicked on, which is stored in this
.
All the rest of the code is stuff we've seen before. The key step is here:
$("#large_image img") .attr("src",bigsrc)
That sets the src
of the large img to the appropriate
value.
The other lines of code are doing similar things for
the alt
attribute, and we also use the alt attribute to
fill in the figcaption
.
The lightbox is pretty similar, except that the destination is large and takes over the screen, using CSS:
<div id="lightbox_display"> <div id="lightbox_inner"> <figure> <img src="../potterpics/pale-yellow-266x200.png" alt="filler image"> <figcaption>A pale yellow image filler image</figcaption> </figure> </div> </div>
Here's the CSS.
#lightbox_display { position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background: black; background: rgba(0,0,0,0.8); display: none; } #lightbox_display figure { background-color: white; width: 75%; margin: auto; } #lightbox_display figure img { width: 100%; }
The key ideas are that the outer box, lightbox_display
, is
fixed and as big as the screen. Inside that is a figure that is slightly
smaller and centered, with a white background. Inside that is an image
where the big version is displayed.
The event handler is very similar to the simple version, except we add
the .show()
at the end to make the lightbox visible.
function openLightboxImage() { var bigsrc = $(this).attr("data-bigsrc"); var alt = $(this).attr("data-alt"); $("#lightbox_display img") .attr("src",bigsrc) .attr("alt",alt); $("#lightbox_display figcaption").html(alt); $("#lightbox_display").show(); }
Of course, since the big version takes over the display, we have to set up a click handler to hide it when the user wants to make it go away:
function closeLightboxImage() { $("#lightbox_display").hide(); } $("#lightbox_display").click(closeLightboxImage);
That's the essentials of how image galleries work.
We now turn to drop-down menus, which will use many of these same ideas:
this
variable
Drop-downs also involve some new concepts:
This will take us a few steps, so be patient.
When we first learned about .hide()
and .show()
, the page would have to be re-arranged based on
whether the target element was shown or hidden. Try the following
example, clicking on the header
list item:
Notice how the page (particularly this paragraph) jumps up and down based on whether the submenu is shown? We don't want that with a drop-down menu.
We solve this by using position:absolute
on the menu,
and positioning it relative to the header. Here it is in action:
The html code looks like this:
And the CSS like this:
The auto
value makes the browser calculate the value to
use, thereby positioning the menu in the same place as it was in the
first example, but using absolute positioning. We added
the height
just so the menu wouldn't overlap this paragraph
when it was shown.
Now let's turn to the JavaScript. Let's start with toggling whether something is open or closed, as we did above. Here's the JavaScript code for the previous example:
The event handler gets the menu, using the selector #ex2
ul
, and determines whether it is hidden. It's hidden when its
CSS is display:none
, which we can determine via an
alternate usage of the .css()
method. We're accustomed
to a 2-argument usage, like this:
$(selector).css("property","value");
Instead we have this:
var curr = $(selector).css("property");
This usage reads the current value of the property. In this case,
we're reading the display
property and putting it in a
variable of the same name. The next lines check the value and if it's
"none", the child is hidden, so we should show()
it,
otherwise, hide()
it.
If we replaced those list items (Harry, Ron, and Hermione) with hyperlinks, we actually have a working drop-down menu:
Presto! A drop-down menu!
Chances are, you will have multiple drop-down menus, like this:
We've done a little CSS magic to lay those out horizontally. Note that
we use several selectors of the form A < B
. That is a
child selector, which is a variant on the descendant selectors that we
already know, but instead of B being any descendant of A, B must be
a child of A.
Let's take a look at the JavaScript code to toggle these menus (at least the first one):
The code is the same as before, but we'd need two more copies of it (suitably modified), one for each sub-menu. Later in the course, if there's time, we'll discuss a better, more abstract way to do this. For now, if you'd like, we'll turn to an important but complicated improvement.
(The rest of this reading is optional.)
There's one small flaw with our drop-down menus, and that is the expectation that users have. If they open a menu and then decide that they don't want to go to any of those pages, they have to click again on the header to close the menu. Most people are used to being able to click anywhere else on the page to close an open menu.
Doing that is cool and worthwhile, but tricky and requires some additional concepts. The first concept is putting an event handler on the whole document (the top of the DOM tree). That event handler will close the open menu. Let's go back to a simpler menu, but with our first attempt at that behavior. Try the following, which doesn't work. We'll soon see why, but for now, let's try to understand this attempt.
Here's the source code for the additional behavior, which adds an event handler on the document that closes everything, so that we can close an open menu by clicking someplace else in the document.
Note that we named the function closeAll
, because it will
close any open menu on the screen. As we've seen, there might be
multiple menus, and this function won't necessarily know which one is
open, if any. We could go to a lot of effort to figure out which menu is
open, or we could just use the power of jQuery to close every one of
them, whether they are open or not. We prefer the simpler approach. That
is to put a CSS class on each header LI and then select every
UL child of those LI elements. We then invoke the .hide()
method on them.
You'll notice that the menu is initially hidden, which is what we
usually want to do with drop-down menus. We do this by invoking
the closeAll
function in our code, above.
We then attach the closeAll
function as a click handler to
the document.
But why doesn't clicking on the header work to open the sub-menu? Is the event handler even running? Let's look more closely at the code:
First, we defined a log5
function. That's just for
debugging: it prints a message to the JS console, along with a counter,
so we can tell one message from another. Next, the same event handler
code as before, only augmented with the log5
calls.
Now, open your JS console and try clicking on the header. You'll see
that the toggle5
function is indeed being invoked,
but the closeAll
function is also invoked!. So,
the menu is opened and instantaneously closed. Why?
The answer is that clicking on the header not only counts as clicking on the header but also counts as clicking on the document.
In retrospect, it makes sense that clicking on the element also counts as clicking on the document, but it's still surprising. This phenomenon is called event bubbling, because an event bubbles up the DOM tree from the leaves to the root, like champagne bubbles going from the bottom to the top of the glass. Every element on the path from the leaf (like the LI) to the root (the document) gets a chance to react to the event. It's also called event propagation.
In this case, the normal event bubbling gets in our way. Is there a way to cancel the event propagation? Yes, but it takes a few steps.
First, our event handler is, in fact, invoked with an argument; it's
just that we've been ignoring it up to now. jQuery ensures that our
event handlers is invoked with an object that represents the event,
including information about what event it was (click, mouse motion,
keyboard events) and stuff like that. That event object has a method
called stopPropagation()
, which does exactly what we want.
So, our new and improved toggle
function is going to
accept an argument that is an event object, and it will use that
argument to stop the propagation of this event up the tree:
Here's the source code for fancy new toggle6
function:
Try it!
You're now prepared to understand all the code in this drop-down example.
In this reading, we'll learn how to build slideshows, both manually advanced and automatic (sometimes called a carousel). We'll also see how to put a random image on a page.
All of these cool JavaScript applications rely on several ideas:
Both of these ideas are implemented in a fundamental data structure called an array.
Arrays are found in every programming language; they're that important. There are sometimes minor differences, but the basic idea is the same:
Imagine that we have a JavaScript program that needs to record some information about Hermione, including her name, age and the courses she is taking in her third year. (The list of courses is not complete.) Here's how we might picture it:
Here's the JS code that would produce that arrangement of data:
var name = "Hermione Granger"; var age = 13; var courses = [ "Astronomy", "Arithmancy", "Ancient Runes", "Care of Magical Creatures", "Muggle Studies", "Potions", "Divination" ];
Try copy/pasting that code into the JS console and then looking at the contents of each variable.
In the previous example, we used the syntax for an array literal, where all the values are known in advance. That's what we'll do most of the time in this course. A few things to note about the syntax:
[]
.
[ "Harry", "Ron", "Hermione" ]
[ "Voldemort" ]
var friends_of_harry = ["Ron", "Hermione"];
var scores = [17, 23, 25, 29, 37];
So far in this course, we have learned about different types of data:
With the exception of dates (and objects in general), each of these seems like a single piece of information. With arrays, we have a collection of information, so we call an array a data structure. Indeed, it is a compound data structure.
So, given the different items in the array of Hermione's courses, how do we extract a particular item? We do that using an index, which is just the numerical position of the item in the array.
Note: the numbering of the items starts at zero. So, item zero in the array above is "Astronomy" and item 6 is "Divination". This is an oddity of Computer Science, but it's quite consistent. (The reason for it is that the low-level implementation is that the index is the distance from the beginning or the number of items to skip to get to the desired one. So, naturally, the first one is index zero.)
Try the following:
var courses = [ "Astronomy", "Arithmancy", "Ancient Runes", "Care of Magical Creatures", "Muggle Studies", "Potions", "Divination" ]; alert( "course 0 is "+ courses[0] ); alert( "course 6 is "+ courses[6] ); alert( "course 2*3 is "+ courses[2*3] ); var index = 6; alert( "course "+index+" is "+ courses[index] );
Note the syntax for getting an item out of the array. We give the name of the variable containing the array, then square brackets enclosing the numerical index of the one we want. In the previous example, we saw three equivalent ways to get element 6 out of the array.
We can even replace an item in an array if we want to. If Hermione drops Divination and replaces it with "History of Magic", we could do this:
var courses = [ "Astronomy", "Arithmancy", "Ancient Runes", "Care of Magical Creatures", "Muggle Studies", "Potions", "Divination" ]; alert( "course 6 is "+ courses[6] ); courses[6] = "History of Magic"; alert( "course 6 is "+ courses[6] );
In our course, we will rarely modify an array. We'll mostly use arrays as numbered lists of information. Nevertheless, it's good to understand. You can think of an array as a bunch of variables, addressed by both name and number.
Arrays also have a built-in property called length
that
tells how many elements are in the array. So, for example, if we want to
know how many courses Hermione is taking, we can execute code like this:
var courses = [ "Astronomy", "Arithmancy", "Ancient Runes", "Care of Magical Creatures", "Muggle Studies", "Potions", "Divination" ]; alert( "The courses array has "+ courses.length + " elements." );
The indexes of an array will be from 0 up to one less than its length. If the length is 7, the indexes will be from 0 to 6.
Note the syntax:
courses.length
The variable has a dot and then the name of the property. This should remind you of the syntax for a method on an object and that's no coincidence, except that we omit the parentheses because it's just a value, not a function.
Let's see another example. Earlier, we learned that the date object would tell us the number of the day of the week, but not the name. Partly, this is because the days could vary with language (English versus French) or with the amount of abbreviation ("Wednesday" vs "Weds" vs "Wed" vs "W").
Suppose we want to map the day number to the day name. We could use arrays for this. Not coincidentally, the fact that days are numbered starting at zero will help us. Consider the following function:
function dayNamesEnglishAbbr(dayNum) { var names = [ "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"]; return names[dayNum]; } alert( "day 0 is "+dayNamesEnglishAbbr(0) ); alert( "day 1+2+3 is "+dayNamesEnglishAbbr(1+2+3) );
This function works like any other, so its argument is still enclosed
in parentheses. The value of the dayNum
parameter is then
used in the square brackets to retrieve the desired name for that day of
the week.
If we wanted to be more careful (perhaps the programmers invoking our
function are sometimes sloppy), we can check that the value is okay. We
can use the length
property here:
function dayNamesEnglishAbbrCareful(dayNum) { var names = [ "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"]; if( 0 <= dayNum && dayNum < names.length ) { return names[dayNum]; } else { return "invalid daynum"; } } alert( "day 13 is "+dayNamesEnglishAbbrCareful(13) );
We now have the foundation on which we can build our slide shows. Here's the basic conceptual structure:
slides
.
currentSlideIndex
.
Note: it's helpful if all the images are the same dimensions (width and height) or at least the same aspect ratio, so that they will all comfortably fit in the target IMG, without the page having to change its layout. Here, we resized all the images to be 1024 x 768 px, which you'll see reflected in the filenames.
Here's the code we'll write:
var slides = [ "harry-potter-1024.jpeg", "ron-weasley-1024.jpeg", "hermione-granger-1024.jpeg" ]; var currentSlideIndex = 0;
The HTML for our slide display is familiar. We put a button on top of it to use for advancing the slideshow.
Let's write a function that will display the slide. Since we kept the
URLs simple, instead of the full relative URL
of potterpics/harry-potter-1024.jpeg
tack on that folder name
now.
function displaySlide() { var file = slides[ currentSlideIndex ]; var url = "potterpics/" + file; document.getElementById("slideshow").src = url; }
We really should do something about the ALT and the FIGCAPTION, but we'll leave that as an exercise for the reader. Hint: you could have an array of values for each of those.
The easy part of advancing the slide is just to increment the
currentSlideIndex
and then
invoking displaySlide
, like this:
// advance and display next slide currentSlideIndex++; displaySlide();
The tricky part of advancing the slide comes when we get to the last slide. In our example, we have just three slides, so the indexes will be 0, 1 and 2, but not 3. What do we do then? An easy thing to do is to start over again at zero. To make the function more general, we can have it use the length of the array, so that if we add another image or two, we don't have to modify this code at all. Here's the function:
// advance and display next slide currentSlideIndex++; if( currentSlideIndex >= slides.length ) { currentSlideIndex = 0; } displaySlide();
This will have the effect of making the slideshow seem circular
,
where it starts over again seamlessly.
The next step is to package this up into a function that can be attached to the button under our figure to act as an event handler. Here's the next step:
function nextSlide() { currentSlideIndex++; if( currentSlideIndex >= slides.length ) { currentSlideIndex = 0; } displaySlide(); }
We then can add a button to the page and attach this function to the button, like this:
< img id = "slideshow" src="potterpics/harry-potter-1024.jpeg" height = 200 alt="" >
Here's the final slideshow:
Later, when we learn about animations, we'll learn a cool effect for transitions between slides.
Implementing a previous slide button is left as an exercise for the reader, possibly in lab.
A carousel or automatic slide show is just one that advances without the user clicking a button. It's a nice effect, though it can also be annoying if (1) the movement distracts from other information on the page, or (2) the user would like to linger longer on some slides than others. Nevertheless, a carousel is often a very nice effect, so let's learn how to do it.
We already have a function that will advance the slide show, namely the
nextSlide
function. If there were a way to have the
browser automatically invoke that function every so often, we'd have an
automatic slide show. It turns out that the browser provides exactly
the tool we need, namely setInterval
.
The setInterval
function takes two arguments:
setInterval
function, not being invoked, so we'll use
the name without the parens after it.
Here's how we would arrange for our nextSlide
function to
be invoked every 2.5 seconds:
setInterval( nextSlide, 2500 );
Let's see it in action:
Here's the final carousel, with no button needed.
In the slideshow and carousel, the code marched steadily through the array from 0 to the end, and then started over again. Something different we can do is to choose a random element of the array. Perhaps you want the opening page of your website to be a little different from visit to visit, so your code will display a random element each time.
To do that, we'll use the following function. This function is not built-in to the JavaScript language or to jQuery, so you'll have to copy its definition to your own .js file. In a later section, we'll learn how the function works, but for now, we'll just trust it.
function randomElt(array) { var len = array.length; var randIndex = Math.floor(Math.random()*len); return array[randIndex]; } /* ================================================================ example */ var myArray = ["H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar"]; alert("Random Element is "+randomElt(myArray));
Try executing the code above a few times. You should get a different element each time.
So, to display a random element each time the page loads, we just have
to employ this randomElt
function in a script element, so
that it runs when the page loads. Remember that the code has to
be after the IMG
into which it will be loaded
exists.
var file = randomElt( slides ); var url = "potterpics/" + file; document.getElementById("randomPicture").src = url;
Here it is in action. Try reloading the page a few times:
The following is a little mathematical, but explains how
the randomElt
function works. Here's the code again:
function randomElt(array) { var len = array.length; var randIndex = Math.floor(Math.random()*len); return array[randIndex]; }
We can describe the operations in 4 steps:
Math.floor
of y, which chops off the
decimal part and gives us one of {0, 1, 2}. Call that
value i
.
i
as the index into the array, returning whatever
element is there.
The following figures illustrate the procedure:
In this reading, we'll learn how the data in a form can be sent to a
server, rather than staying in the browser. In web development, the
browser, tablet, phone or other device that people work directly with is
often called the front end
, in contrast to the servers, databases,
and other invisible infrastructure that is called the back
end
. Forms and Ajax are one of the primary ways that the front end
communicates with the back end.
When we introduced forms earlier in the
semester, we didn't talk about any of the attributes of
the <form>
element. Here is how it will usually look
like:
<form id = "form2" action = "some-script" method = "GET/POST" > <!-- Here go all elements of the form, such as <input>, <button>, etc. --> </form>
We have seen the id
attribute before, so, let's focus on
the two other attributes, action
and method
.
There are two methods of sending the data to the server using the HTTP protocol: GET and POST. The main difference between GET and POST is how the data is sent to the server.
The two forms below are identical and they have the same value
for action (referring to the
script simple-script.php
), but one uses method=GET
and the other
method=POST
. Try them out and look at the URL bar to see
the difference.
You probably saw something like this, depending on how you filled the form out:
Because submitting forms generates an HTTP request, you can use the Network tab in the Inspect Element window of the browser to inspect the details of sending forms, including how the data is packaged to be sent to the server.
In this course, we'll stick to POST, but the default is GET, so make
sure you always specify method="post"
in your forms.
Where does the form data go? What happens to it? These questions are
mostly outside the scope of this course, but we'll give you a peek at
it. The data goes to a server somewhere on the internet, wherever you
specified in the action
attribute of the form. Once there, it
gets processed by some script (a program running on the server).
That script could do anything (update a database, charge your credit card, transmit instructions to someone at a warehouse, etc.) In this course, we won't explore those further. We will, however, look at some scripts that can do a few simple things.
A widespread, server-side language called PHP can be used to process
the form data sent from a browser and email it to someone. To avoid the
PHP script being exploited by spammers, the PHP script will have the
destination hard-coded
into the script. That is, the form data
can only be sent to a destination of your choosing, not the sender's.
This is perfect for emailing a "contact us" form to your organization's
secretary, emailing the contents of an order form to your organization's
fund-raising chair, and so forth.
We will not burden you with having to learn PHP just to use this
ability. Instead, we'll provide you with a mechanism for creating a PHP
script that sends email to your desired recipient
(the to
address). It will work a lot like the
following, except that you won't be able to specify the
recipient. (Here, we've limited the appeal to spammers by fixing the
text that is sent.).
Fill out and submit the form below, and then check your email.
(Here is the form in its own web page.)
In order for the script to access your name and address, the form needs
to contain an input named from_name
and another
named from_email
. Here is the code of our simple form:
The problem with the first above-shown technique is that it redirects the user to a new web page. In fact, every time a form is submitted, the browser page is automatically either refreshed, or redirected to the result sent by the server. In the example above, the response was fairly ugly (JSON notation), while in practice we would make the result look a lot prettier. Nevertheless, it will still be a new page.
Sometime we might desire this, but sometimes not. In fact, it is quite common to expect to get notifications on the same page that we are already using. For that purpose, we will use a technology named Ajax.
Ajax, which stands for "Asynchronous JavaScript and XML", is a way of
passing data in a structured format between a web server and a browser,
often without the user being aware of such a transaction, because there
is no page refresh or redirection to a new page. Thus, with Ajax and
some DOM manipulation, you can load or reload only a portion of the page
with jQuery and JavaScript. It's spelled either AJAX
or Ajax
; we'll use the latter.
To think about Ajax in action, consider Facebook: when you like an item, that doesn't just update the page you're looking at; it sends information to scripts running on FaceBook's servers to record that fact in a database, so that it can count and display the number of likes. The browser sends that information using Ajax, so that nothing interferes with your reading of the page. When you comment on a post, you're filling out a little form and submitting it to Facebook's servers, again using Ajax. When Facebook sends you new updates without your having to re-load the page, that's Ajax again.
(Although when the technology was introduced, the XML format (a relative of HTML) was used for exchanging data between the server and the client, nowadays, JSON (Javascript Object Notation) is the preferred format. This is what we will use as well. In fact, you might have noticed that the ugly non-Ajax response above was, in fact, JSON notation, which is a variant of our JS Object literal notation. The main difference you've seen is that property names are in quotation marks.)
The way Ajax works is summarized in the graphics below:
The Ajax technology is implemented within the jQuery library and can be used easily in our Javascript applications. In this course, we won't explore Ajax in all its glory, but we will use it to gracefully send email using a special PHP script on the server.
We can use Ajax to send data to a back-end server in the same way
as method=post
in our form.
Let's first see it in action. Here is the previous RSVP
example, with the addition of a button to submit the form via Ajax. Try
both! (In real life, we would probably remove
the type=submit
button, rather than have both. But for now,
we want to compare the behavior.)
You can also use the form in its own web page: Deathday form Ajax.
First, let's look at the HTML:
You'll notice that we added a type=button
button. We also
created a place on the page to put the response from the back-end
server. We don't have to do that; we could signal the user in some other
way, or we could completely ignore the response.
Now, let's examine the JavaScript code:
The first line just makes sendmail
the event handler for
the Ajax button, which is just a plain button
, rather than
a submit
button.
The sendmail
function prepares for just one call to the
jQuery .post()
method. Notice that
the .post()
method doesn't have a selector, because it's
not operating on a particular element, but instead is doing an Ajax
request, submitting the form using POST
.
The .post()
method takes four arguments:
action
attribute.
handleResponse
function will get
and process the response. More on this in a moment.
It takes us a few steps to get ready for
calling .post()
. First (line 14), we retrieve
the action
attribute. Then, on lines 15 and 16, we pull two
values out of the form. On line 17, we construct a JS object out of both
of them. Finally, we call .post()
.
The JSON response from the back-end will be converted into a JavaScript
object, and the handler function will be invoked with that object. So,
let's look at the handleResponse
function next.
As we know, that function gets invoked with a JS object. In this case,
the object will contain two properties: status
and text
. The status
property has two possible
values: "ok"
and "fail"
, both strings. The rest
of the handleResponse
function is straightforward code using
a conditional to check whether the status is "ok"
, and if it
is, giving a cheery green notice, and other wise giving notice in scary
red. In either case, the notice is just the text
of the
response, inserted into the response_element
.
The next step is to discuss a more general kind of back-end script, than the script above that is only good for accepting an invitation to Nearly Headless Nick's Deathday party. The script we'll discuss is one that will send email to a predetermined address, such as the secretary of your student org or the sales clerk for your fund-raiser — whoever should get the email that your website will allow people to send.
In class, we'll talk you through the process of creating and
downloading a special-purpose PHP script that you can put on your back-end
server (in the same folder with your HTML pages) that can be
the action
for your forms. It will have an email address
of your choosing embedded in it.
Here's an example of the kind of email script we will use.
You'll notice that it takes four fields:
wwellesley@gmail.com
or ccustomer@hotmail.com
.
The last two are self-explanatory, since all of us are very experienced with email. The first two are sorta new, because that's usually handled by our email software, like Gmail or whatever. Here, we need them because the person visiting our website is otherwise anonymous. (In fact, we can't prevent them from lying to us, or sending useless emails; let's hope that doesn't happen.)
When we write our JavaScript to use this PHP script via Ajax, we'll have to construct a JS object using those four properties. Like this:
var what = {from_name: "Harry Potter", from_email: "hpotter@hogwarts.ac.uk", subject: "My Scar", body: "Dear Padfoot, my scar's been bothering me lately ..."}; $.post(where, what, ...);
Of course, we don't want to restrict our forms to those four fields. We'd like to have forms where we can ask to be added to an email list, order Weasley Wizarding Wheezes, or any of many other things we might want.
To do that, we're going to build on our knowledge of how to
do Madlibs. Think of the madlib as the body of the
formatted email message that we want to send. We can write JavaScript code
that will take our arbitrary form, insert the key data into a hidden
element much like our MadLibs story, then extract the whole contents of
the MadLibs using jQuery's .html()
method. (Or, better,
use .text()
method, which will give us just the text,
omitting all the HTML markup.)
The following form demonstrates the idea. It really sends the data to a back-end script, but this script just describes the email that would have been sent (but doesn't really send one). So, feel free to try this as much as you'd like. The response text is printed to the JS console; open it up to see.
Here's the madlib
letter that we will send. In real life, we
would use display:none
in CSS or the
jQuery .hide()
method to make this text invisible, but you'll
want to see it:
Dear Fred and George,
Please send by return owl the following:
fainting fancies
fever fudge
nosebleed nougat
puking pastilles
Signed ()
Finally, of course, you should take a look at the JavaScript. You can
see that we've added one new function, formatLetter
, which
looks a lot like your MadLibs code. The placeOrder
function
first calls formatLetter
, and then proceeds as it did before,
except that now the body
comes from the formatted letter.
Modularity is something that has come up several times in this course, in different guises. We'll discuss this important principle both in general and how it specifically might apply to the web sites we'll build.
In this reading, we'll be looking at several ways to make your website more modular — i.e., created out of reusable parts that make the website easier to maintain.
Reusability
has become one of the watchwords of the
environmental movement, and for good reason. Reusing objects that have
taken much time, energy, and other resources to create is eminently
sensible.
Reusability makes a lot of sense in the programming world, too. Suppose that a programmer has spent a lot of time developing code that solves a problem. This code can often be reused by the same programmer in a different program, or by other programmers in their programs.
In JavaScript, we've seen that functions are a good way to make code reusable. Once code is encapsulated in a function, that function can be invoked in many different contexts.
Groups of related functions and data structures are often collected together in reusable units called modules or libraries that are used by large communities of programmers. These play an important role in the adoption of programming languages. Languages like Java, C/C++, Fortran, and Python have become popular in large part because of the impressive modules/libraries that have been built for these languages. jQuery is a library that makes JavaScript programming easier.
Modules are particularly reusable when they have been designed to be used in mix-and-match ways — think Legos, standard parts from a hardware store, USB peripherals, mix-and-match clothing, etc. That's the idea behind the logo at the top of this section. Crafting a module that is easy and useful to reuse is more art than science, but it's a worthy goal.
As a concrete example with web pages, suppose that you've figured out how to make a beautiful navigation bar for one page in a website. You'd like a way to package up your navbar code into some sort of module that you can reuse on other pages for the same site, and perhaps for pages in other sites. What you don't want to do is simply copy & paste your navbar code into other pages, because that's a nightmare to maintain. Which brings us to the topic of ...
What is maintenance and why is it important? We all know what, say, car maintenance is: changing the oil, keeping the tires inflated, fixing stuff that breaks. It keeps your car running better and longer. But that doesn't have anything to do with web sites, since they don't have physical parts that wear out.
But they do need to be changed and updated from time to time, which
computer scientists have come to call maintenance.
Countless
studies have shown that, over the lifetime of a software project, about
two-thirds of the overall cost is in maintenance. Surprising, but true.
You may not be maintaining your cs110 project, but someone will, and we
should make their job easier.
You're already doing some of the most important steps:
The techniques we will see today are similar to the idea of an external
style sheet, because they allow us to put shared code in a single file
that is used by more than one page.
This general idea is known by computer scientists as modularity:
a module
is any kind
of distinct, separable thing that can be used more than once.
As we saw when we introduced JavaScript, very often you have some JavaScript code that you'd like to use on several web pages. The best example would be a collection of useful functions, all stored in a single file. You could have the browser load the file of functions, and then your web page could invoke the particular functions you want. Let's look at some examples.
The top of this page features a set of font sizes formatted in various
sizes, where clicking on them changes the font-size for this page to the
selected size. We're calling this the accessibility bar
(like a
navigation bar, but with accessibility controls). All the code is in a
separate file, so the only thing we had to do to put the accessibility bar
on this page was the following:
<script src="accessibility-bar-jq.js"></script>
Let's look at the contents of the accessibility-bar-jq.js file. You don't have to read every line, but the first three functions are important (lines 20-45). The rest of the code defines function to add clickable items to the page.
Some key things to look at and think about are:
Another example is some JavaScript that processes a form. Here's an example:
The HTML for the form is straightforward. We'll learn about this a bit
later in the course, so don't worry about the details. The main point
is that we can get the amount of the bill from the user, rather than
usingprompt()
.
The JavaScript code associated with this form is the following, which
uses the jQuery .val()
method to get the value that the
user filled into the form. Again, we'll cover this a bit later, so
don't worry about the details.
(The change()
method attaches a handler that is
invoked whenever the value in the form changes. We'll learn about event
handlers very soon.)
As you've probably guessed, the taxRate
variable and the
calculateTax
function are defined in the external JavaScript
file, tax-calculations.js:
The latter part of this example assumes that the form, in addition to
the bill
input, also has outputs named
tax_amount
and total_amount
. When the user
fills out the bill amount, the JavaScript code calculates the other two.
It puts the values into form inputs so that they can be sent to the CGI script
if desired.
The calculateTax
function is defined in the
tax-calculations.js
file. One nice effect of this technique
is that if the tax rate changes (say, the state legislature declares a tax
holiday), we can change the definition of the function in that file, and
any web page that uses the file will immediately start using the new
definition.
If we create a file of JavaScript code for reuse in other web pages, it
could just be a collection of random, unrelated, and disorganized
functions. There's nothing in this mechanism to enforce any kind of
conceptual organization to the JavaScript code. You could simply have one
file, say mystuff.js
, and put anything you want in it.
That would work, but it has the disadvantage that you probably will end up loading code you don't need (a small drag on page loading time). More importantly, it's harder to find the code you want and to keep things organized. Instead, you could divide your code into conceptually coherent groups. For example:
<script type="text/JavaScript" src="date-functions.js"></script> <script type="text/JavaScript" src="form-validation.js"></script> <script type="text/JavaScript" src="rollovers.js"></script> <script type="text/JavaScript" src="lightbox.js"></script>
These files are more worthy of the term module. If someone
asks to use your lightbox
code, you can point them to that file,
and they won't have to wade through irrelevant stuff to use your code.
(Unfortunately, dividing your code into several small files increases the loading time of your website, since the browser needs to make a separate connection for each one. Therefore, professional web designers use tools to combine a bunch of modules into one file, thereby getting the best of both worlds.)
Suppose you're the one who implemented the
tax-calculations.js
module above, and other web designers
are using your code on their websites. You know that the variable
taxRate
is the rate that we pay, and so can anyone reading
your code. However, you worry that someone might mistakenly or
maliciously set that variable to the wrong value. If the true tax rate is
5 percent, they could set the variable to 5 (which would be a 500 percent
rate) or accidentally set it to 0.50 instead of 0.05 (just a typo), or
even set it to a non-numerical value.
To avoid these mistakes, one thing you could do is provide a programmatic way to set the tax rate, rather than just assigning to the variable. For example, something like this:
function setTaxRate (newRate) { if( isNaN(newRate) ) { alert("Error: the new value is not a number: "+newRate); } else if( newRate < 0 ) { alert("negative tax rates don't make sense"); } else if( newRate > 0.3 ) { alert("This tax rate is higher than any legal rate "+newRate); } else { taxRate = newRate; // finally, set the global variable. } }
Then, people set the tax rate using this function, instead of just
assigning to the variable taxRate
. Of course,
our setTaxRate
can't check for every possible error, but it
could be a help.
But, you object, a malicious person could still set the
taxRate
variable directly, instead of using this function.
That's true. However, there are programming techniques (which we won't go
into) that protect values absolutely, so that you must go through
a setter
function like this in order to set the value. Thus, our
module can build up walls around itself, controlling how it is used.
Computer scientists endeavor to create programming languages that are powerful, easy to use, and also encourage and enforce useful modularity. This makes programming better and more effective.
Suppose we have three pages, A, B, and C, all of which should share a common header (with banner image and navbar) and common footer.
We've already studied one way that this common markup can be shared
among the pages, namely using the jQuery .load()
function
to load the code from a master copy, probably on the main page. In the
following sections, we'll describe a second approach.
We would like to be able to describe these pages in such a way that we generate the shared parts so that they are easy to change. For example, to add a new menu item or footer item, we should only have to change one piece of code, not code on each of the pages.
Another way to achieve the modularity we desire in the A, B, C example is to use JavaScript functions to generate the shared HTML code in each file.
As shown below in the HTML source code for A.html
, we can
accomplish this using the JavaScript functions addHeader()
and addFooter()
. (The code for B.html
and C.html
is exactly the same except for a different content
paragraph.)
The
file header-footer-jq.js
contains definitions of
addHeader()
and
addFooter()
, which are responsible for generating the HTML
elements that are shared between all the pages.
Any change to the functions addHeader()
,
addBannerPic()
,
addNavbar()
,
addMenuItem()
,
and addFooter()
will affect all of the web pages in the website.
For instance, it's easy to add a new menu item to the menu bar
or a new icon to the footer list.
See the header-footer example in action
When sound is transmitted or stored it may need to change form, hopefully without being destroyed.
Sound moves fast: in air, at 340 m/sec = 750 miles per hour. Its two important characteristics are Frequency (aka pitch) and Amplitude (aka loudness). Frequency is measured in Hz or cycles per second. Humans can hear frequencies between 20 Hz and 20,000 Hz (20 KHz). Amplitude is measured in deciBels (we will see later that it is approximated with "bit-resolution").
Consider music:
A similar kind of story can be told about visual images (sequences of static images) stored on videotape or DVD and played on your home VCR or DVD player.
Any time signals are transmitted, there will be some degrading of quality:
When we continue to transmit and transform signals, the effect is compounded. Think of the children's game of "telephone." Or think about photocopies of photocopies of photocopies...
This is the transmitted signal:
and this is the received signal (dashed) compared to the transmitted signal:
The horizontal axis here is time. The vertical axis is some physical property of the signal, such as electrical voltage, pressure of a sound wave, or intensity of light.
The degradation may not be immediately obvious, but there is a general lessening of strength and there is some noise added near the second peak.
There doesn't have to be much degradation for it to have a noticeable and unpleasant cumulative effect!
The pictures we saw above are examples of analog signals:
An analog signal varies some physical property, such as voltage, in proportion to the information that we are trying to transmit.
Examples of analog technology:
Analog technology always suffers from degradation when copied.
With a digital signal, we are using an analog signal to transmit numbers, which we convert into bits and then transmit the bits.
A digital signal uses some physical property, such as voltage, to transmit a single bit of information.
Suppose we want to transmit the number 6. In binary, that number is 110. We first decide that, say, "high" means a 1 and "low" means a 0. Thus, 6 might look like:
The heavy black line is the signal, which rises to the maximum to indicate a 1 and falls to the minimum to indicate a 0.
The signals used to transmit bits degrade, too, because any physical process degrades. However, and this is the really cool part, the degraded signal can be "cleaned up," because we know that each bit is either 0 or 1. Thus, the previous signal might be degraded to the following:
Despite the general erosion of the signal, we can still figure out which are the 0s and which are the 1s, and restore it to:
This restoration isn't possible with analog signals, because with analog there aren't just two possibilities. Compare a photocopy of a photocopy ... with a copy of a copy of a copy of a computer file. The computer files are (very probably) perfect copies of the original file.
The actual implementation of digital transmission is somewhat more complex than this, but the general technique is the same: two signals that are easily distinguishable even when they are degraded.
The main point here is that digital transmission and storage of information offers the possibility of perfect (undegraded) copies, because we are only trying to distinguish 1s from 0s, and because of mathematical error checking and error correcting.
If digital is so much better, can we use digital for music and pictures? Of course! To do that, we must convert analog to digital, which is done by sampling.
Sampling measures the analog signal at different moments in time, recording the physical property of the signal (such as voltage) as a number. We then transmit the stream of numbers. Here's how we might sample the analog signal we saw earlier:
Reading off the vertical scale on the left, we would transmit the numbers 0, 5, 3, 3, -4, ... (The number of bits we need to represent these numbers is the so-called bit-resoluton. In some sense it is the sound equivalent to images' bit-depth.)
Of course, at the other end of the process, we have to convert back to analog, also called "reconstructing" the signal. This is essentially done by drawing a curve through the points. In the following picture, the reconstructed curve is dashed
In the example, you can see that the first part of the curve is fine, but there are some mistakes in the later parts.
The solution to this has two parts:
In the example above, it's clear that we didn't sample often enough to get the detail in the intervals. If we double it, we get the following, which is much better.
In general, finer resolution (bits on the vertical axis) and faster sampling, gets you better quality (reproduction of the original signal) but the size of the file increases accordingly.
How often must we sample? The answer is actually known, and it's called the Nyquist Sampling Theorem (first articulated by Nyquist and later proven by Shannon). Roughly, the theorem says:
Sample twice as often as the highest frequency you want to capture.
For example, the highest sound frequency that most people can hear is about 20 KHz (20,000 cycles per second), with some sharp ears able to hear up to 22 KHz. (Test yourself with this Online tone generator or this hearing test.) So we can capture music by sampling at 44 KHz (44,000 times per second). That's how fast music is sampled for CD-quality music (actually, 44.1 KHz).
The size of an uncompressed audio file depends on the number of bits per second, called the bit rate and the length of the sound (in seconds).
We've seen that there are two important contributions to the bit rate, namely:
As the sampling rate is doubled, say from 11KHz to 22KHz to 44KHz, the file size doubles each time. Similarly, doubling the bit resolution, say from 8 bits to 16 bits doubles the file size.
As we've seen, the sampling rate for CD-quality music is 44KHz. The bit-resolution of CD-quality music is 16: that is, 16-bit numbers are used on the vertical axis, giving us 216=65,536 distinct levels from lowest to highest. Using this, we can actually calculate the bit rate and the file size:
bit rate (bits per second) = bit-resolution * sampling rate
file size (in bits) = bit rate * recording time
For example, how many bits is 1 second of monophonic CD music?
16 bits per sample * 44000 samples per second * 1 second = 704,000
Therefore, 704,000 / 8 bits per byte = 88,000 bytes ≈ 88 KB
That's 88 KB for one second of music! (Note that there are 1000 bytes in 1KB, so 88000/1000 is 88KB.)
And that's not even stereo music! To get stereo, you have to add another 88KB for the second channel for a total bit-rate of 176KB/second.
An hour of CD-quality stereo music would be:
176 KB/sec * 3600 seconds/hour = 633,600 KB ≈ 634 MB
634 MB is about the size of a CD. In fact, it is not accidental that a CD can hold about 1 hour of music; it was designed that way.
Consider the following form to compute bit-rate and file size. Fill in the missing function definitions.
What are the practical implications of various choices of sampling rate and bit-resolution?
Bandwidth over the internet cannot compete with the playback speed of a CD. Think of how long it would take for that to be downloaded over a slow modem.
So, is it impossible to have sound and movies on your web pages? No, thanks to sound compression techniques. We have seen how GIF and JPG manage to compress images to a fraction of what they would otherwise require. In the case of sound and video, we have some very powerful compression file formats such as Quicktime, AVI, RealAudio and MP3. Read more about the history of MP3 (or history of MP3).
The tradeoffs among different compression formats and different bit rates are explained well in this 2007 article on audio formats from the New York Times. (This article is available only on-campus or with a password.)
A discussion of the technology behind these compression schemes is beyond the scope of this course. They are similar in spirit to the JPEG compression algorithm, in that they are lossy compression schemes. That is, they discard bits, but hopefully the bits that least degrade the quality of the music?
Some compression algorithms take advantage of the similarity between two channels of stereo, so adding a second channel might only add 20-30%.
What do you think?
A condensed version of these notes can be found here.
Note that beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.
Suppose we have a really bad burst of static, so a 1 turns into a 0 or vice versa. Then what? We can detect errors by transmitting some additional, redundant information. Usually, we transmit a "parity" bit: this is an extra bit that is 1 if the original binary data has an odd number of 1s. Therefore, the transmitted bytes always have an even number of 1s. This is called "even" parity. (There's also "odd" parity.)
How does this help? If the receiver gets a byte with an odd number of 1s, there must have been an error, so we ask for a re-transmission. Thus, we can detect errors in transmission.
You can see some examples of parity using the following form. The parity bit is the last (rightmost) one, with the red outline.
Assuming even parity, what is the parity bit for each of the following:
With some additional mathematical tricks, we can not only detect that a bit is wrong, but which bit is wrong, which means we can correct the value. Thus, we don't even have to ask for re-transmission, we can just fix the problem and go on.
Try it with the following JavaScript form. Type in a number, and it will tell you the binary code to transmit. Then, take the bits and add any single-bit error you want. (In other words, change any 1 to 0 or any 0 to 1.) If you click on "receive," it will tell you which bit is wrong and correct it. If you think I'm cheating, you can type the bits into another browser!
Note: for technical reasons, the parity bits are interspersed with the data bits. In our example, the parity bits are bits 1, 2, 4 and 8, numbering from the left starting at 1. (Notice that those bit position numbers are all powers of two.) So, that means the seven data bits are bits 3, 5, 6, 7, 9, 10, and 11.
transmitbutton
receivebutton.
What if more than one bit is wrong? What if a whole burst of errors comes along? There are mathematical tricks involving larger chunks of bits to check whether the transmission was correct. If not, re-transmission is often possible.
The error correcting code we saw above may seem a bit magical. And, indeed, the algorithm is pretty clever. But once you see it work, it becomes somewhat mechanical.
Here's the basic idea of this error-correcting code. (This particular code is a Hamming code. The Hamming (7,4) code sends 7 bits, 4 of which are data. The (7,4) code is easy to visualize using Venn diagrams. The general idea is this:
For more detail, see this general algorithm
Solution to the Exercise with Form for Bit-Rate and File Size is here.
(Reading: With these notes, we strongly suggest you read Blown To Bits Chapter 6, Balanced Toppled: Who Owns the Bits.)
The results of intellectual work are covered by three distinctly different legal areas:
(A rant by Richard Stallman on the very term intellectual property is worth reading.)
A trademark is the recognizable mark that a company has made to market its products. It is not only the name of the product that is covered by the trademark (e.g., Coca Cola), but also the shape (e.g., the recognizable Coca Cola bottle shape, or the font of the Coca Cola name used on the product). This means that one cannot create, say, LTCola and have it bottled in a Coca Cola-shaped bottle or have its name written with a Coca Cola-looking type of font. All famous marks are registered and watched by webcrawlers. Domain names that confuse origin or “dilute the value of the mark” will be challenged by the owners (even in ridiculous cases such as the “Christmas Blend” of Starbucks.)
The work of an inventor is covered by a patent. A patent is issued after careful review by the patent office, which must be persuaded that the work described in the patent is not a trivial extension of an existing work (“prior art”).
In your project, it is not likely that you will produce or infringe upon a patent. You might violate a trademark if, for example, you decide to have the picture of a a Coca Cola bottle on your page or you use the same font, color, and style to write something. What you need to pay particular attention, though, is on the Copyright Law.
Copyright is about the rights of the author, artist, or creator.
What rights does an author have? Suppose you're the author of a novel. You can:
Of course, as the owner of the copyright, you can decide to sell or lease the rights, piecemeal or in total, to other people. Musicians often sell their rights to the record company. Sometimes in the fine print at the end of a movie, you'll see “for the purposes of copyright, the owner of this film is Paramount Pictures, Inc.”
Note that copyright applies to things, in the sense that your creative endeavor has to be fixed in some medium: paper, videotape or whatever. You can't copyright an idea; that idea has to be expressed somehow.
Copyright law didn't always exist. Copyright actually began in England in 1557. It was a royal decree that allowed only designated printers to publish. That way the government controlled what was printed (censorship) and at the same time protected the market for the designated printers. With the end of censorship, the notion of copyright evolved into the Statute of Anne (1710), which is considered to be the forerunner of modern copyright. The full title of the law says,
An act for the encouragement of learning, by vesting the copies of printed books in the authors or purchasers of such copies, during the times therein mentioned.
Copyright was not granted primarily for the benefit of authors, but so that authors would have an incentive to create works, thereby benefitting society. Of course, now the record and movie industries have powerful lobbies in Washington, so copyright law is being extended to benefit these big corporations. They are also jealously protecting their copyrights.
Copyrights don't last forever. For a long time, the law was that copyright protection ended 70 years after the author's death. It's actually more complicated than that, depending on whether the author is anonymous or whether a corporation now owns the copyright (see the resources section, below).
The movie industry got the copyright law extended so that the movies that were released in the 1920s, where the copyright was about to expire, are still copyrighted. Now the copyright is held by the employer for 95 years since publication or 125(!) years from creation.
When a work is no longer copyrighted, it is in the public domain and therefore is owned by society in general. This is where society really benefits from the new works created by copyright law. Once something is in the public domain, the author no longer has any rights and anyone can do with it what they want. For example, someone wrote West Side Story without having to pay any royalties to Shakespeare's estate, because Romeo and Juliet is now in the public domain. Similarly, anyone can perform the works of J. S. Bach, because he's been dead for more than 70 years.
Note, though, that a performance of a work is copyrighted. So, if the Wellesley Widows perform a 17th century art song, they own the copyright to their performance, even though the song is in the public domain.
In fact, some authors will create things and explicitly place them in the public domain, giving up their rights so that others can immediately make derivative works, building on the contribution.
What is copyrightable? An original work of intellectual authorship. This has a rather low threshold (many things can fall into this category). But facts, ideas, titles, short phrases, and public domain information are not copyrightable.
How do you copyright something? Do you have to write the © symbol on the work and register it with some office somewhere? No, the 1971 Berne convention, an international convention of most countries in the world, declared that any work of yours is automatically copyrighted unless you explicitly place it in the public domain. (The U.S. joined the Berne convention in 1989.) If you made it; you control it. This holds whether or not a copyright notice is attached and whether or not the work is registered. Of course, adding those steps indicates that you intend to protect your rights, so it's not a bad idea.
The author doesn't hold complete power over of her work. There is an important legal concept of fair use, so that society can gain some benefits immediately. Examples:
How can you know whether something is “fair use”? There is no easy answer. The only real answer can be found in a court, but we can say what the court's decision will be based on [from http://www.wellesley.edu/Library/copyright.html ] the following 4 criteria:
An important related point is that copyright is different from plagiarism. Most of us know that we have to quote properly in our research papers and give credit where credit is due. If I quote from your research paper, I have to give you credit. However, if I've quoted so much of your work (chapters and chapters) that I get into trouble with copyright, giving you credit isn't going to help. After all, that bootleg CD of Britney Spears isn't passing itself off as someone else's work; that wouldn't be the point.
What does copyright law mean for you and your web site? It means that you should assume anything you find on the internet is copyrighted and that you may not use it without permission of the author. Certain things are intended to be copied, and the author may well explicitly grant permission for that. For example, snippets of code at a JavaScript download site are obviously intended to be used in your web page. You should do so with a clear conscience. Academic honesty requires that you give credit to the web site, but that has nothing to do with copyright.
For the purposes of your homework or project in this class, not giving credit might constitute plagiarism. Ask your instructor if you are not sure.
Medium | Example | Holder |
---|---|---|
Modern Text |
Screenplays, books, poetry, quotes, journals, newspaper articles |
Author or publisher |
Images |
Stills, video, artwork, logos |
Photographer, object owner, artist, architect, trademark company |
Sound |
Performance rights, mechanical rights, synchronization rights |
Lyricist, Performer, Studio, Composer |
Software |
Patents, university employees, trade secrets to 3rd party |
Programmer, University faculty, students, 3rd party |
People (pictured or described) |
Actors, Recognizable People, have rights of Publicity, of Privacy, against defamation |
individuals, agents, parents |
Similarly, when you are putting things on the site, you may need to think about copyright. Say there's a cartoon you'd like to put on it. It might be a cartoon that your client (a professor) always uses in lecture. Is this fair use? Look back at the criteria, and you see that (1) the class may be educational, but the primary purpose of the cartoon is probably just to be amusing; (2) the work is probably highly creative; (3) you've used the entire cartoon; and (4) the cartoonist sells this for money, and if it's free, globally, on the internet, there goes the market. A court would probably rule that this is not fair use. (On the other hand, cartoons are ephemeral, the cartoonist makes a new cartoon every day, so one cartoon might not dent the sales of the latest Dilbert compendium.) It also depends on the artist/cartoonist. Bill Watterson and his syndicate has zealously protected Calvin and Hobbes for many years. Scott Adams probably protects Dilbert just as fiercely, but might not.
One thing to keep in mind is that the internet is global . Thus, the market effect of putting something on the web is a zillion times greater than just using it in a campus course. (Thirty students versus a billion on the web.) One approach to this is to restrict access to copyrighted material, say via a password or something.
Copying digital information is extremely easy. This encourages people to freely share digital music, videos, books, software, etc., even though such sharing may violate copyright laws.
Digital Rights Management (DRM) refers to technological and legal methods of protecting the copyright of digital information. On the technology side, digital information may be associated with "copy protection" or "content protection" systems that try to prevent the information from being copied. For example, some distributors of digital music use a combination of DRM information in music files and DRM software in the music players to limit the number of times the music file can be copied or played or the duration of time in which it can be played. Software available for trial download uses DRM to ensure that the software can't be used after the trial period expires. DVD video discs use a Content Scramble System (CSS) to encrypt the contents of the discs as an attempt to prevent the discs from being copied or being played by devices not licensed by the DVD Copy Control Association.
Software-only approaches to DRM can be circumvented by skilled technologists, who often publish descriptions of how to circumvent the DRM or applications that circumvent the DRM. In response, the computer industry has developed hardware-assisted approaches to Trusted Computing, whose goal is to prevent uncertified programs from manipulating copyrighted digital information. A popular incarnation of this idea is the Trusted Platform Module (TPM), a chip that uses cryptography to verify that the operating system and applications manipulating the DRM-protected digital information have not been compromised. This chip is also known as the "Fritz chip" after former Senator Ernest "Fritz" Hollings, who championed its adoption. Many computers sold today are equipped with a Fritz chip.
Because many forms of digital information must ultimately be converted to analog forms in order to be displayed, DRM systems suffer from an analog hole that fundamentally limits their efficacy. End users can ultimately copy these analog audio/picture/video forms, though such copies may suffer from reduced quality.
Although DRM technology can often be circumvented, it is not legal to do so in the US. This is a consequence of the Digital Millenium Copyright Act (DMCA), a 1998 US law that includes an anti-circumvention provision that outlaws devices and methods for bypassing DRM. Even telling others how to circumvent DRM appears to violate this provision. Even providing a link to a CSS descrambler might be illegal, but CMU CS professor David Touretzky doesn't think so! The DMCA was championed by the music and film industries. Free speech advocates claim that the DMCA is at odds with the First Amendment, and critics call it Digital Millenium Competition Avoidance.
Today, we'll talk about how machines can communicate in a secure way. A little while ago, we talked about analog and digital signals, and this lecture will remind you of that one, but we're talking about a higher level: not just how information gets from point A to Point B, but how it can get there without anyone in between knowing what is being said.
Lots of us had fun with secret codes and such when we were kids. We'd get secret decoder rings in our cereal boxes and make up messages to send to each other. Could be useful if they were intercepted by the teacher! We hope that today's lecture evokes that sense of fun.
A condensed version of these notes can be found here.
When we discuss encryption, it's traditional to name the people involved, just so we can keep things straight. The cast of our little drama is:
The story is that Alice is sending Bob a message through some insecure medium (not whispering in his ear): think postcard or radio. Alice might be a field agent and Bob is her contact at HQ. Alice and Bob might not even know each other: perhaps Alice is trying to persuade Bob to defect, and Eve is trying to find traitors.
The messages might be sent by radio, or phone, or email, or carrier pigeon. All media are potentially insecure. Radio waves can be picked up by other receivers, phone wires can be tapped, as can computer networks. Even carrier pigeons can be captured.
One approach is to put the message in code. Humans have invented lots of codes in history, and code-making and code-breaking have often been pivotal. Indeed, Alan Turing and his code-breakers in England may have done more to win WWII against Hitler than General Patton, because they were able to break the codes sent with the Enigma machine used by the Germans. Similarly, the Navajo Indians, who used their native language as an unbreakable code in the war in the Pacific, contributed greatly to the war effort without ever firing a shot.
Terminology:
Caesar codes are named because Julius Caesar used them to communicate with his troops, so they're hardly new. They're also not particularly hard to break, but if you have to write your message on a postcard, it's better than nothing. The idea is letter substitution. A popular and easy way of doing it is rotating: Imagine the letters of our message are each printed on a wheel, and we simply turn the wheels so each letter is replaced by one that is n letters before or after it in the alphabet. For example, we replace each letter with the one preceding it in the alphabet (and “A” is replaced with “Z”).
Plaintext:
IBM
Ciphertext:
HAL
(You may remember that the computer in Arthur C. Clarke's classic science fiction novel 2001: A Space Odyssey was the HAL 9000. Many people believe this was a thinly veiled reference to IBM, using a -1 rotation code.)
Caesar actually used a rotation of 3, so A was written as D, B as E, and so forth. Thus, “ATTACK” would be sent as “DWWDFN”.
Another popular Caesar code is called rot13 and has been used for many years on the Internet as a way to hide things from inadvertent reading, such as movie spoilers or dirty jokes. You have to decode it using rot13 to read the spoiler or the dirty joke. Rot13 is just a Caesar code incrementing each letter by 13. This is convenient because you can decode using the same software as encoding, since rotating twice by 13 brings a letter back to where it started.
If you want to encrypt using a Caesar code by hand, you can use the
following form. Choose an amount to rotate by, then write your message
using the letter from the cipher alphabet instead of the one from the
plain alphabet — in other words, the one below the one you
really mean. For example, setting rotation
to 1, you can
see “B” below “A” and “C” below
“B” and so forth. Setting it to 25, you can see
“H” below “I” and “A” below
“B” and “L” below “M”, giving
“IBM” for “HAL”.
Use the form above to translate some simple words into code. Translate the first two using Caesar's code (rotate by 3) and the last by rot13.
plaintext | ciphertext |
---|---|
BY SEA |
|
BY LAND |
|
ATTACK |
|
You can check your answers and try out other Caesar codes using the following form. Note that this form converts the string to uppercase. Also, it only rotates the letters; it leaves the numbers, punctuation and other characters alone. It could be easily re-written to handle more stuff.
So, now you and a friend can exchange secret messages, say by email, just by encrypting them using this form. You just have to agree on a rotation amount (and keep it secret!).
Earlier, we ignored punctuation and anything other than a letter. Obviously, that makes for a bad code. What we really should do is treat all characters the same. How is this done? First, we have to say what a character is.
As you know, everything in a computer is just numbers. How does it deal with characters? As you remember from an earlier lecture, early computer designers agreed on a set of characters and a standard number for each one. For example, A is represented with 65. One such system was ASCII: American Standard Code for Information Interchange. Here's part of the ASCII code:
You read this table just like an addition table. Add together the row and column headers, and you find the numerical value (ASCII code) of the character at the intersection of that row and column.
Note: The ASCII code is not about encryption; it's just a standard for numbering characters. The existence of such a numbering means that we can do rotation codes numerically, like this:
encoded_char = (plaintext_char + rotation_amount) % 128;
ASCII is now being supplanted by UNICODE, which is a vastly larger code, designed to handle all the world's languages.
Breaking
a code (that is, reading encrypted messages without
having the secret information) is
called cryptanalysis.
Caesar ciphers are relatively easy to break, as is any cipher based just on substitution of letters. For example, the most common letter in the ciphertext is probably the encypted form of a very common letter, such as “e” or “t” or “a”. With more sophisticated statistics, and trial and error, both of which computers are good at, it's fairly easy to crack a substitution cipher.
One way to spoil the statistics of a Caesar cipher is to use multiple Caesar ciphers. In other words, suppose we rotate the first letter of the plaintext by 2, the second letter by 0, and the third letter by 19. Then we repeat, rotating by 2, 0, 19, until we're done with the plaintext. Such a technique is called a Vigenere Cipher, mis-attribute to Blaise de Vigenère from the court of Henry III of France in the sixteenth century. It was considered unbreakable for some 300 years!
Another way to think of the Vigenère Cipher is as follows. Write
down a keyword, such as “cat”. Then, use the
index of each letter as the amount to rotate. For example, if the
keyword is “cat”, then we will use a 2 rotation, a 0
rotation, and a 19 rotation (“t” is the 19th
letter of the alphabet if you start from zero: A
is the 0th letter). Write down the keyword above the
plaintext and use it to select the correct rotation. It helps to have
a table of all the rotations:
Here's the idea. To encrypt the message:
ATTACK AT DAWN
using the keyword “CAT”, we write the keyword above the message repeatedly:
keyword
CATCAT CA TCAT
plaintext
ATTACK AT DAWN
ciphertext
Now, below, we compute each letter of the ciphertext by looking at the intersection of the row of the keyword and the column of the plaintext. (In fact, because of the way the table is set up, it doesn't matter whether the keyword character is the row or the column.) In our example:
keyword
CATCAT CA TCAT
plaintext
ATTACK AT DAWN
ciphertext
CTMCCD AM DTYN
Notice:
Use the table above to encrypt “ATTACK” using the Vignere cipher and the keyword “CODE”. Notice how the A's and T's never have the same substitutions.
Because a Vigenère cipher uses more than one substitution alphabet, it's one of a bunch of ciphers known as polyalphabetic.
Choose a partner (say from the row in front of or behind you, so you can't easily look onto her monitor, though it doesn't really matter). One of you will be the sender and the other the receiver.
Notice that the form above only encrypts lowercase letters; it removes anything else, including spaces. This is partly for pedagogical reasons (since the ciphertext is also all lowercase letters) and partly for simplicity, since the JavaScript code doesn't have to worry about dealing with non-printing characters in the output (ciphertext). However, we can simply represent each character as its ASCII code, as a two-digit hex number. That results in this vigenere form for all characters.
The Germans in WWII used an encryption scheme based on a polyalphabetic cipher. They built machines to do the encryption and named them Enigma machines. They considered the encryption by the Enigma machine unbreakable, and relied on it to communicate with their U-boats in the North Atlantic. The Enigma machine looked like manual typewriters with keys and hammers, but internally the keys were attached to drums that did the substitutions by “re-wiring”: mapping the keys to different hammers. Actually, there were three (later four) rotors that did the substitutions and after each letter was typed, the rotors turned like a car odometer (the rightmost fastest, and so forth).
The British managed to steal several of the machines and figure out how they worked. (One attempt to steal one was the story of the movie U-571, except that in the movie the Americans stole the machine, but in real life the British were the heros.) Unfortunately, they still needed the keyword (the settings of the rotors). A group of mathematicians led by Alan Turing (and including quite a few women) were able to analyze the transmissions and, eventually, crack the code. This was a major turning point in the war, and the Allies went to enormous effort and sacrifice to conceal the fact that they had cracked the code.
Here are some links about the Enigma machine, in addition to the Wikipedia article on Enigma.
Now that we understand something about encryption, let's go back to our initial scenario and see how it works. Alice wants to send a message to Bob, without Eve or anyone else being able to read the message. She encrypts it using the secret key, and sends it to Bob. Even if Eve intercepts the message, she won't be able to read it. Bob uses the secret key to decrypt the message and read what Alice says.
Suppose that Alice is behind enemy lines and Bob is back at home base. She gets to a radio transmitter, gets out her secret code book, encrypts her message using today's key, and sends it to Bob. Bob has to get out the matching code book to decrypt the message. If Eve is able to capture the code book, disaster!
If Eve captures the code book, Alice needs to send a new secret key to Bob. She needs to send it securely, so that Eve can't read it. Thus, we're back at square one: Alice needs to send a key securely to Bob, so that she can send a message securely to Bob. Consider the secret codes we used earlier, when we said you could send email messages in code, just by agreeing on a rotation amount, but if you had to send the rotation amount by email, you're stuck!
For thousands of years, this was the essential paradox of encryption: you had to have a secure way of communicating in order to have a secure way of communicating. A real-life chicken-and-egg problem!
The kernel of the paradox is that all of the encryption methods that we have discussed so far are kinds of private key systems: they required a shared private key.
In 1976, Whitfield Diffie and Martin Hellman solved the problem of deriving a shared private key over an insecure channel, an invention that came to be known as Diffie-Hellman key exchange. While it contains some cool ideas that are still in use today, we won't purse it further in this course. (If you're interested, there is a good Wikipedia article on Diffie-Hellman key exchange.) Instead, we will discuss the RSA cryptographic system, which has some very interesting properties.
The year after Diffie and Hellman came out with their cryptographic system, three MIT professors (Ron Rivest, Adi Shamir and Leonard Adleman) created the first published, practical implementation of public key cryptography. The main ideas of a public key cryptosystem are:
How does this help? Very simple: Bob knows that Alice wants to send him a message, so he creates a pair of keys. He advertises his public key all over the world (maybe he puts it on his web page). Alice sees it, and so does Eve. Alice downloads it and uses it to encrypt her message. She radios it to Bob, and Eve intercepts it, but Eve can't decrypt the message — only Bob can. No one but Bob can decrypt the message, because only Bob has the secret key.
Imagine that we send information around in lockable trunks (rather than, say, paper envelopes).
Private Key | Public Key |
---|---|
The preceding description is relatively straightforward, but the implementation is not. Nevertheless, several different public key systems have been created. One system, called the RSA method, involves advanced number theory that can cause grown men to burst into tears (or maybe it's just me), but the idea is very cool. (If you're interested, please consult the Wikipedia article on the RSA method, but be warned that the article assumes a good deal of knowledge of number theory, though there are good examples and references.)
hard.
The last fact makes the RSA method difficult to crack. For their contribution, the members of the RSA team received the 2002 TURING AWARD, which is considered the Nobel-equivalent for Computer Science.
There are many more substantive and accurate sources to learn about the RSA method, but here are a few steps that give a few more details to make it more concrete.
To generate a key pair, do the following:
To encrypt, you compute
c = me (mod n)
To decrypt, you compute
m = cd (mod n)
The public key is e and n. (Yes, there are two numbers in the public key.) The private key or secret key is d.
To crack this code, you have to factor n, or find some other way to compute d from e and n.
There are many implementations of the RSA method, but a very nice one that shows the steps of generating a key pair and also allows for encrypting and decrypting is Herbert Hanewinkel's RSA Public Key Encryption Demo
We said that breaking RSA is hard,
but what does that mean,
really? You have to factoring n, a large number. The state of
the art is little better than trying all the possible factors to see if
they divide evenly into the number. Suppose the number is 256 bits long,
which is roughly 76 decimal digits long. Let's call it 80 digits. How
many numbers would you have to try? All the primes up to the square root
of the 80 digit number, and the square root will have 40 digits. So, it's
about
10,000,000,000,000,000,000,000,000,000,000,000,000,000
Yes, but computers are fast, right? Suppose we have a fast computer that can try a billion factors every second. A billion is a 1 followed by 9 zeroes, so to divide by a billion, we just knock off nine digits. Then we only have to wait for
10,000,000,000,000,000,000,000,000,000,000
seconds. Since there are about 32 million seconds in a year, this will only take
312,000,000,000,000,000,000,000
years.
The centerpiece of the RSA method uses numbers that are the products of two very large prime numbers, forcing Eve to buy very fast computers and wait a very long time to crack the encryption.
When you access a secure web server, your browser and the server become Alice and Bob. They want to communicate securely, but they don't know each other and they didn't make prior arrangements to have a secret key. Instead, they use the idea of public key encryption. When the little padlock icon closes, your browser has established a cryptographically secure connection to the server, and so your credit card numbers and other private information is safe from the Eves on all the network connections between you and the server.
Is your information safe? Not entirely. Maybe someone can hack into the server. Maybe someone at the destination isn't trustworthy. Maybe the server is a complete fraud, put up by Mallory. Maybe someone was looking over your shoulder when you typed. There is probably no such thing as perfect security. We all have to decide how much effort to put into security.
Encryption isn't just for web traffic. It could also be used for email and for telephone messages, including cell phones. Right now, anyone with a radio scanner and a little know-how can listen to cell phone conversations. Similarly, tapping a phone is very easy. Encryption lets us use any of these insecure media to send a private message.
Furthermore, note that public key encryption solves one important failing of private key encryption, namely the key distribution problem, but private key encryption is not obsolete. The reason is primarily speed. Public key algorithms are very slow, much slower than private key systems.
Therefore, the way things usually work in practice (say, by HTTPS) is a hybrid approach:
session keysuitable for use by some private-key algorithm.
Suppose Alice wants to send Bob the message:
Call off the attack, it's a trap! Signed, Alice
She encrypts her message with Bob's public key and radios it to him. Meanwhile, Mallory, who is malicious, sends Bob the message:
Go on with the attack, it's all clear! Signed, Alice
She also encrypts the message with Bob's public key and radios it to him. She's pretending to be Alice! What is Bob to think?
There's a cool aspect to public key encryption that we haven't mentioned. We said that one key decrypts what the other encrypts. In fact, both keys can encrypt, and the two keys are opposites, which means that one can decrypt what the other encrypts.
So, here's what Alice does: she encrypts her message with her own private key. Bob gets it and successfully decrypts it with Alice's public key. (She has a web site with her public key listed.) He then realizes that only Alice could have sent this message, since only her private key can create a message that her public key can decrypt.
Thus, public key encryption can give us digital signatures . The purpose of a regular, real-life signature is that, presumably, only you can sign your name the way you do. By comparison to a known signature on file, your bank or any other interested party can verify that something has been signed by you.
The “signing” of digital certificates, which we discuss in the lecture on certificates, actually uses digital signatures just like this. It just involves a different use of public key encryption: a trusted third party signs the public key, using its own private key! This is confusing, so think about it for a while...
Similarly, if there was a public key on file for a person, they can use their private key to “sign” an electronic document, even an email.
In practice, because public key systems are slow and verbose, people do not digitally sign the orginal document. If you have a 50-page long legal contract, you don't want to use public key to sign it, because the result would be 50+ pages of gibberish and would take a long time to compute.
Instead, they sign a digest of the document. What is a digest? In the field of cryptography, a digest of a document is something that
the customer owes the bank $100to
the customer owes the bank $500, the digest changes.
(I'm tempted to say that the digest depends on the document, the whole document, and nothing but the document.)
Thus, for the purposes of digital signatures, it's just as good to sign a digest as the original document, and it saves a lot of time and space. Here's what happens:
signsthe digest.
Many message digest algorithms have been invented. As of this writing, cryptographic experts recommend using one of the algorithms in the SHA-2 family of algorithms for digital signatures. (The algorithms differ in the number of bits in the digest, such as 224, 256, 384, and 512.)
You can play with a JavaScript implementation of these SHA-2 algorithms.
We've been taking the side of Alice and Bob, helping them keep information away from Eve. What if Alice and Bob are bad guys and Eve is the government? What's the point of getting a court order to tap a phone line if the conversation is encrypted? What can Uncle Sam do?
Throughout the 90s, the U.S. government did several things:
There was a lot of resistance. Computer manufacturers didn't want to have separate international and domestic versions of software. Privacy advocates and civil libertarians worried about the government's power to invade privacy. (The keys would be split in half with the halves held by different agencies, to prevent a single agency running amok and violating civil rights.) Practical people pointed out that the bad guys wouldn't use crypto systems that the U.S. government had the keys to; they'd use foreign-made crypto systems. Eventually, the government caved in, but in our current climate, the desire to clamp down on cryptography will rise again.
What do you think?
Note that beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.
Many more sophisticated private-key encryption schemes have been invented since Vigenère and Enigma, and research continues on new algorithms. (Recall that private key or symmetric encryption is still in constant use, in addition to or combined with public key schemes.) One private key algorithm, not even a particularly new one, is called Triple DES, because it is based on the older DES — Data Encryption Standard. If you want to encrypt a message using a private key system, and you want to use something better than the Vigenère form that we provide above, Triple DES is a reasonable choice.
Here is a link to a form that allows you do encrypt using Triple DES.
How to use it:
inputand click the appropriate button below the box. The output appears below.