This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Labs

1-2-Day In-Class Assignments

1 - Lab 1 - File Publishing and Validation

Due: . In this activity, you will publish a web site!

In this lab, you’ll practice using the web server infrastructure that will be available throughout this course.

Background

Throughout this semester, you will frequently need to transfer data to w3stu.cs.jmu.edu, the JMU CS web server for students. (Note that w3stu is the same as stu. When accessing your pages through a web browser, you will use w3stu; when transferring files, you will use stu.) As a CS student, you already have an account.

The easiest way to get started transferring files is to use FileZilla, a free, cross-platform, open-source file transfer application. From that site, download the program on your laptop or wherever you will be doing your development work.

The JMU CS Wiki has a page showing some FileZilla basics 🧠that perhaps you should check right quick. Once you have FileZilla installed, you will connect to stu.cs.jmu.edu using the sftp:// protocol as shown in this screenshot, setting your username and password as appropriate. The first time you connect to stu, you will get the “Unknown host key” pop-up box as shown; click “OK” to continue.

Make sure that the Host: file is sftp://stu.cs.jmu.edu and not just stu.cs.jmu.edu. The default behavior for FileZilla is to use the insecure ftp:// instead of sftp://. This will not work, as stu does not accept incoming ftp:// connections.

Once connected, the right-hand side will show a folder path on the "Remote site:" that looks like /cs/home/stu/your_user_name. This screenshot shows an example. Under the "Filename" list, scroll down until you see the "www" directory shortcut. Double-click on it and you will see something that looks like this screenshot. Notice that the "Remote site:" has changed to /cs/www/stu/your_user_name, which is the base directory of your web site.

Any file that you drag-and-drop into this directory will be published on w3stu automatically. If you copy a file called foo.html into this directory, you can access that file by pointing a web browser to the URL https://w3stu.cs.jmu.edu/your_user_name/foo.html. Within FileZilla, you can also create subdirectories to organize your site’s pages.

Note: If you are comfortable with working on the command line, you might try doing this lab using something like scp or rsync. You can find plenty of tutorials for either by searching. The scp command line would look something like:

$ scp index.html <i>your_user_name</i>@stu.cs.jmu.edu:~/www/index.html

File Publishing

Your first task is to download and publish the following files based on the naming conventions shown below. Right-click on each to save a copy to the desktop on your computer.

  1. index.html
  2. data.txt
  3. linked.html

Step 1: Publish index.html

Open the index.html file in a text editor and change your name in the <meta> tag that indicates the “author” of the file. Then transfer the index.html file so that it can be accessed at the URL https://w3stu.cs.jmu.edu/your_EID/cs343/lab1/. Note that you will need to create the cs343 directory in your www directory and lab1 in cs343.

Test this step by going to the URL in your web browser. If you click on any of the links, they should not work (giving you a 404 error).

Step 2: Publish the other files

Transfer the data.txt file so that it is in the same directory as the index file. Click on the "text file" link from the index file and confirm that you can access it.

Transfer the linked.html file, but you'll need to create a subdirectory and rename the file in the process. Once you've transferred the file, it should be in the www/cs343/lab1/linked subdirectory and renamed as index.html.

When you are finished, you should have the following files and directory structure, and all links in the index file should work:

Validation

Throughout the semester, your lab and project submissions need to successfully pass the W3C Nu HTML Validator. The provided index.html file does not pass. Copy and paste the link to your published version and fix the error based on the feedback there. Do the same for the linked/index.html file. Note that there is one error there (use of <tt>) that cannot be fixed based on what we have discussed so far.

Submission

  1. Download this file (named site.txt) and edit it with vscode. You should only need to replace your_EID with your JMU EID.
  2. Submit your modified version of the site.txt file to the Lab 1 assignment on Gradescope.

2 - Lab 2 - Document Structure with HTML

Due: . In this lab, you will use HTML to “markup” plain text into a structured document.

In this lab, you will be formatting a text document as an HTML version of this PDF. (Ignore the page break in the middle of that PDF, since HTML pages don’t have page breaks.) All of the text content is provided in the starter code below. You just need to format the document as HTML.


Starting Code

Use the provided lab2.txt to avoid copying and pasting large chunks of text from the PDF. Lines beginning with # are intended as guidelines, and should be converted into HTML comments.

<!-- as a reminder HTML comments begin with those 4 characters at the beginning of this code sameple and end with the following 3 -->

Step 1: Basic HTML Documents

Your task is to add HTML elements to the provided text file to create the document structure. Note that the PDF was generated using a word processor and we are only using HTML. As such, some appearances (fonts, spacing, etc.) may not be exactly the same. You should try to make it as close as possible, however.

For this step, you need to have a correctly formatted header to create a valid HTML file. You also need to format all paragraphs and lists correctly.

You could approach this either by:

  1. renaming your downloaded copy of the provided lab2.txt to index.html and working directly there, or
  2. creating a new file called index.html and copying selections from lab2.txt into it as you proceed through the lab.

Step 2: HTML Document Structure

Once you have all the previous components, you need to add the remaining HTML elements, such as the title, subtitle, and horizontal lines. You also need to add the JMU logo image at the bottom. The only things that should be missing at this point are the portions controlled by JavaScript.


Step 3: JavaScript Fundamentals

Once you have added the required HTML elements, you will need to add JavaScript code in a few places. Each of these will require a separate <script> tag:

  1. Just after the (opening) <body> tag, create the variables as described in the lab2.txt file.
  2. Use one variable as a Boolean to determine whether or not to show the JMU logo image at the bottom.
  3. Use the other variables as Strings that contain both the level 3 heading tags and the corresponding text.

Submission

In addition to the formatting of your source code itself (which you should ensure vscode has done, using the defaults), your submission must also validate cleanly with no errors or warnings.

Note that publishing your file is not necessarily to test validation. The easiest way to test validation is to install a browser extension/add-on. Search in your browser’s extension tool for “HTML Validator” (by Marc Gueury, you should already have this if you completed the Dev Setup). You can also copy and paste the HTML into the validator itself (selecting the “text input” from the “Check by” drop-down list).

You will submit your index.html file to Gradescope for submission.

3 - Lab 3 - Bootstrap

Due: . In this lab, you will try out the Bootstrap design framework.

Overview

As we begin to look at how to specify the presentation of our webpages using CSS, this sneak peak is intended to help give you a sense of how much can be accomplished with CSS. (We will also write “vanilla” CSS that begins with no third-party code so that you know how these things are accomplished, and importantly how to modify them to suit your specific needs.) In this lab we introduce Bootstrap, but there are many design frameworks out there that provided excellent CSS starting points. For novices especially, design frameworks like Bootstrap are a great way to begin projects you intend to publish because typically the frameworks:

  1. are well-documented
  2. are well-tested
    1. in multiple browsers
    2. on multiple devices
    3. with many viewport sizes
  3. have solved issues novices aren’t even aware of yet
  4. have designed for accessibility
  5. have designed for responsiveness
  6. have designed for usability
  7. have designed for performance
  8. demonstrate best practices for CSS that works well with semantically-structured HTML (itself a best practice)

Specs

(Following the steps in later sections) Make a web page that has:

  1. a responsive navigation bar ("nav bar") at the top,
    • one that has a “Responsive behavior” called a “Toggler” and With a brand name shown on the left and toggler on the right
  2. a responsive content area after the nav such that:
    1. when the viewport is at least 768px wide,
      1. the left 2/3 of the content area’s will be the main content area
      2. the remaining right 1/3 of the content area will be an area for aside content
    2. when the viewport is less than 768px wide,
      1. the main content will occupy the full width of its container
      2. the aside content will come after the main and will also occupy the full width of its container

Starting Resources

Code

Bootstrap’s documentation has the perfect starting code for you ready to roll. We recommend you use the (first) code sample from #2 in the Quick start section (for now, you can ignore the other code samples on that page including the second code block in #2).

This seems like it’s become difficult to specify clearly. This is a good starting point:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
  </head>
  <body>
    <!-- your code here -->

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
  </body>
</html>

❤️ Favicon

By now, you’ve seen that most of what goes in an html document’s head is metadata not immediately visible to the visitor, and that the content that will be rendered into the browser’s viewport is in the body. The icon that will appear by default alongside the site’s title in browsers’ tabs and which will be used in browsers’ histories and bookmarks is often called a favicon.

This wesbos character has put a ton of useful resources on the web. While I appreciate a fun tone as much as the next prof, I think flicking off visitors on entering your site isn’t the way 🫣 I’d’ve done it. Nonetheless, FavFarm is pretty useful. Consider just referencing this example for now:

<link rel="icon" href="https://fav.farm/🕸️">

Placeholders

Placeholder text is often useful when you’re focussed only on the semantic structure and/or presentation of your page, but not yet its content. A common choice is lorem ipsum text (for the curious: there’s always more to learn), which you can grab a small example in vscode by simply typing lorem and accepting the emmet completion (via hitting tab or enter).

There’s also a delightful variety of these generators, e.g.:

for text
🥓 Bacon Ipsum
🧁 Cupcake Ipsum
🚀 Space Ipsum
🧀 Cheese Ipsum
🏴‍☠️ Pirate Ipsum
➕ and 🤪 more
for images
🏞️ Lorem Picsum

Step 1: Begin with the provided resources

  1. Start this activity as we generally recommend you start all of them
  2. create a file named index.html and paste the code sample from the bootstrap docs (linked above) into your otherwise empty index.html file. Make sure that your code includes the link and script tags referencing the Bootstrap files.
  3. paste the provided favicon link into your head (see above).
  4. delete the Hello, World heading.
  5. make doctype all caps.

Step 2: Add some filler

So that you can see how the content of the is affected by the subsequent steps,

  1. create a main element as the first child of the body element
  2. create an h1 with the text content Bootstrap demo as the first child of the main element
  3. paste 3+ paragraphs of your favorite lorem ipsum text (each paragraph should be wrapped in a p element) into the main of your document (immediately following the h1).
  4. create an aside element as the second child of the body element
  5. paste 3 more paragraphs of your favorite lorem ipsum text (each paragraph should be wrapped in a p element) into the aside of your document.

Step 3: Add a nav bar

  1. Copy the 2nd example in the Bootstrap Components:::Navbar::Responsive:Toggler, the one described as With a brand name shown on the left and toggler on the right.
  2. Paste the copied code to be the first child of the body in your document (i.e. it should immediately precede the main element you created in the previous step).

Step 4: Add a content area

  1. After the close tag of your nav element, let’s add a container for the rest of our page’s content. Paste the following code after the </nav>:
    • <div class="container">
      </div>
      
  2. Move both the main and aside elements you created in the previous step into the div you just created. Sometimes doing something like this might be more simply specified as “wrap the main and aside elements in a div with the class container”.

Step 5: Implement responsive behavior

Bootstrap implements several pre-defined responsive “breakpoints”. In this use of the term, a breakpoint is not a place to pause code when using a debugger, like most of our prior uses of the term. Rather, it means a value in a range that is used as the threshold for a change in presentation/behavior.

To accomplish the specs, we need to figure out where a viewport width 768px falls in the range of Bootstrap’s breakpoints. According to the docs, viewports with a width of at least 768px are considered “medium” which is represented in bootstrap’s classes with the abbreviated md.

  1. Wrap the main and aside elements in a div with the class row. (So you’re adding only one div element, but it will have two children: main and aside.)
    • so this new div should be the only child of the div with the class container you created in the previous step.
  2. Add this class to your main element: col-md-8, which means that at viewport widths of at least 768px, the main element should be presented as a “column” (within the row you just added) that takes up 8/12ths of the width of its container.
  3. Add this class to your aside element: col-md-4, which means that at viewport widths of at least 768px, the aside element should take up 4/12ths of the width of its container.

Step 6: Accordion

In Graphical User Interfaces on your computer, phone, watch, fridge? contact lens?!, you have become accustomed to some common ways for information to be presented and common dynamic behaviors of those presentations, let’s call a representation of information and its dynamic behavior a “widget”. Bootstrap implements many commonly used “widgets” as components. One of them is an accordion. Let’s try out the accordion.

  1. copy the first code example from the accordion docs and replace all the paragraphs in your aside with the code you copied.

Going Further

Color the navbar

  1. read the docs and try to apply the primary color scheme to the navbar
    1. if you succeed, you may agree that the navbar looks nice, but now the search box looks weird. I think it looks better to make one more modification in this case: find the form in the navbar and add this attribute and value to it: data-bs-theme="light"
    2. oh dang, now the search button kinda sucks too huh? I think it looks better to make one more modification in this case: find the search button in the navbar. Notice it has the class btn-outline-success? drop the -outline part of that class name. Dang, this is still pretty low contrast… 🤔 know what, make it btn-light. That seems alright.

Give the navbar margins to match content

  1. in your nav element, you’ll see one of the first children (which contains all the other descendants of nav) is an element with class container-fluid. Remove the -fluid part of that class name. Read more on Bootstrap Containers if you like.

Currently the responsive behavior of the main content area is based on the md breakpoint, which is at 768px, but the navbar’s responsive behavior is based on the lg breakpoint, which is at 992px. This means that the navbar will be in its “collapsed” state when the viewport is between 768px and 992px wide, but the main content area will be in its “2 column” state. This is a little weird. How can you fix it?


Only show aside when there’s enough real estate

  1. read the docs (e.g. these about hiding elements) and try to make it that the aside is either hidden (on viewports narrower than 768px) or shown on wider viewports as previously specified (a column that takes 4/12 of the available width).
    • if you want an additional challenge, make it that the aside is (1) hidden on extra small (narrower than 576px), (2) shown on small (from 576px up to 767px) but placed below the main, or (3) placed in a column on wider viewports.

4 - Lab 4 - CSS and Presentation Fundamentals

Due: .

CSS Basics

In this lab, you will use CSS to make a page look better. Specifically, you will create three CSS files that progressively add better aesthetics to a provided HTML file. These CSS files will be added incrementally to the HTML files to layer on improved spacing, colors & borders, and a customized navigation menu.

All code must validate cleanly (no errors or warnings) through the standard validation tools listed below. You must resolve all errors and warnings before submission. For individual CSS properties, please refer to the MDN reference list.


Starting Code

Start by extracting provided source code and opening the files in your preferred text editor:

You will only submit the CSS files, so you will not be able to make any changes to the HTML files. All visual changes must be controlled by CSS only. All CSS rules are based solely on elements, not ID or class.


Step 1: Spacing

Your first task is to define element rules to add spacing throughout the page. You'll also make heading elements use a sans-serif font instead of the default font. For this step, modify spaces.css to define rules that use only the following CSS properties (or variations, such as margin-top, padding-left, etc.):

  • font-family
  • margin
  • padding
  • vertical-align
  • width

Add rules for the <h1>, <h2>, <header>, <main>, <nav>, <p>, and <section> tags to create this layout. Your solution may have slight variations, but it must satisfy all the constraints in this annotated version. Unless specified otherwise, spacing should be in terms of the viewport height and width, with values at least 1% of the viewport dimension. (Note that you can use 0px to refer to any spacing that needs to be zero.)

In the rules for <main> and <nav>, add the rule display: inline-block;. (We will discuss this property next week.)


Step 2: Colors

Your next task is to define element rules to add borders and colors throughout the page. For this step, you will modify colors.css and you may only use the spacing rules from above and the following additional CSS properties and their variations:

  • background
  • border
  • color

Add rules for the <body>, <header>, and <section> tags to create this layout. As before, your solution may have some variation but you must satisfy all the constraints in this annotated version. The borders specified must be at least 2 pixels wide. Note that the curved corners are created by using the border-radius property.

This step also links to the spaces.css to reuse the spacing values from the previous step. You will also need to set body margins in colors.css to make sure that the header covers the full width and top of the page.

In this and the next step, you will need to use combinations of the following colors:

  • #450084
  • #dacce6
  • #dcdcdc
  • #dfd2aa
  • #f4efe1
  • #f5f5f5

Step 3: Customized Navigation Menu

Your final task is to specify custom rules to the navigation menu on the left part of the page. For this step, you will modify navs.css. You must use some descendent or child selectors so that you do not change elements outside of the navigation. For this step, you will need the following CSS properties (in addition to ones mentioned previously):

  • font-weight
  • list-style-type
  • text-align

Add rules for the elements within the <nav> structure to create this layout. You must satisfy all the constraints in this annotated version. Make sure that your rules apply only to the specified element. In particular, be sure to distinguish between the list (<ul>), list item (<li>), and link (<a>) elements.


Submission

When you are finished, create a css.zip file containing only the CSS files. Do not have the files in a subdirectory (folder) within the .zip file. Submit your css.zip file to Gradescope.

5 - Lab 5 - CSS: Layout

Due: .

Layout

In this lab, you will use CSS and JavaScript to make a reactive page that changes layout dynamically. You will use a combination of fixed and flex-box display properties, then use buttons to change what classes are controlling the layout.

All code must validate cleanly (no errors or warnings) through the standard validation tools listed below. You must resolve all errors and warnings before submission. For individual CSS properties, please refer to the MDN reference list.

Starting Code

Start by extracting provided source code and opening the files in your preferred text editor:

For this lab, you will need to modify all three files and submit them to Gradescope.

Semantic structure and fixed navbar

  1. Start with the index.html file. Create a nav element containing an unordered list. Each item will contain a button with an id attributed as specified in the file. No classes or other attributes should be added to any of these elements, as all presentation and dynamic features will be added using CSS and JavaScript.
  2. Inside the main, add 2 randomly selected SQUARE img elements (200x200). Use picsum.photos to get the URLs for these images. Make sure to add alt text.
    • At this point, you should see a list with three bullet points with some ugly buttons next to them. Below that you should see two random images with a little bit of spacing around them.
    • Add a link element to the head to reference styles.css and reload the page. What changed?
  3. In the styles.css file, start by getting the header and main into position. To do this, specify the appropriate properties to the header and main rules (including display and position) according to the comments. Set the color variables in the :root rule and give the header a background so you can see it.
  4. Wow, those buttons look bad! Let’s fix them.
    • Use descendant selectors to update the nav list and list items. Get rid of the bullet points, add a right margin, and switch the list items to be in a row rather than a column (i.e., switch them from block to inline).
    • They’re still kind of ugly. Use a descendant selector to update the button style properties. Add some padding and increase the font size. Give them no border but use border-radius to curve the corners. Use those spiffy color variables you defined up in :root.
    • OK, now we’re making progress.

Dynamic buttons

  1. Buttons look and feel so much better when they appear to react when we move the mouse over them or click on them. Let’s take care of that.
    • Add a new rule for the button when it is in the :hover state. When this happens, give the button an outline that is at least 3px in size and matches the text color of the button.
    • We won’t be able to use it yet, but add another rule based on the button also having a class called active. In this class, switch the text and background colors of the button.
    • That’s starting to look nice. In this image, the “Spread Out” button has the active class while the mouse is hovering over “Rotate Direction”.
  2. The :hover pseudoclass is automatically built into the web browser, but we need to do some work for the active class. It’s JavaScript time!
    • Add a script element to index.html to reference flex.js then open this file.
    • The default implementation shows a pop-up box when you click the “Spread Out” button. Get rid of that and replace it with code that adds or removes the active class we created a bit ago. To do this, we’re going to manipulate the spreadButton.classList. Rather than keeping all the classes in a string (which you would do in HTML), the DOM maintains them as a list.
    • If you write more than one line of code here, you’re doing it wrong. Yes, you can add some logic this:
      if (spreadButton.classList.contains('active') {
        // Button has class="active" so remove it
        spreadButton.classList.remove('active');
      } else {
        // Button doesn't have the class so add it
        spreadButton.classList.add('active');
      }
      
      But really? Why? Just use toggle, which does that work for you.
    • If you click on the “Spread Out” button, it should turn dark. Click it again and it goes back to the original coloring. If it works, add some more code to do the same thing for the “Rotate Direction” button. You’ll need to add a new variable at the top of the file, then add code similar to what was provided for the spreadButton toward the bottom of the file.

Flex

  1. Toggling button colors is cool, but it’d be even better if the buttons…actually did something? Let’s start with spreading things out.
    • Head back to styles.css. Create a new rule that only applies to the main element when it has the “spread” class. This rule will use the space-around value to add spacing to elements along the main axis.
    • Give it a try to make sure you’ve got the flex-box set up properly. If you load page, the images should be centered vertically and pushed all the way to the left edge of the window. But if you add the class="spread" to the main element in the HTML and reload the page, they should be spaced out horizontally. Good? Good. Now, remove class="spread" from your HTML. We’re controlling that in other ways.
    • Back to flex.js. We need to add a new mainElement global variable and use document.querySelector to grab the main. (Note that we are NOT adding an event listener to main, so don’t copy that line of code.)
    • Just like the buttons have a classList, main does, too. Add another line of code to your spacing function so that it toggles the “spread” class.
    • Click the “Spread Out” button a few times. The images should switch back and forth from the default positioning on the left to spaced out horizontally. And the button should still be inverting colors each time.
  2. Now let’s add some rotation.
    • Add some more CSS to switch the flex-box to a column when the “vert” class is present for the main element. Just like spreading things out, add some JavaScript code to toggle this class.
    • Notice what happens if you click both buttons to activate both classes. Can you explain why? Does it matter which class you turn on/off first?

DOM manipulation

  1. We’ve still got one more button hanging around, so let’s do something with it.
    • Add a new global variable and the associated initialization code at the bottom of the flex.js file to reference the button.
    • In addImg, use document.createElement to create a new img element. Set the src and alt attributes to grab another random square image. HINT: HTML attributes just become object fields in the DOM; so imgElement.src can be used to view or change the <img src="..."> field.
    • Reload the page and click the “Add Image” button. Nothing happens. Huh. Oh, that’s right, we created the img element, but we didn’t tell the browser where we wanted the image to be added. Use appendChild to add it as a new child of the main element.

Going further

  1. The original index.html file may have looked a bit odd. It was missing closing tags for head, body, and html. It turns out, that’s perfectly fine and valid. The HTML5 spec indicates that certain tags, including closing tags, are optional.
  2. If you add enough images and make the flex-box into a column, notice that the header bar covers up the images. What CSS rule could you add to fix this?
  3. What if we don’t like one of the random images? Let’s find a way to delete it. In the initialization code, use document.querySelectorAll to get all of the images. This function returns an array, so you can do images.forEach to loop through them. Use this to add an event listener to call removeImg for each one. In this function, call remove on the passed parameter. This won’t work. Use the console to print out the element that gets passed and try to figure out the problem. You can then read the textbook chapter on events to try to fix it.

6 - Lab 6 - Grids

Due: . Responsive mode: TINY! 🥹 then dark 🌒

In this week’s prep, you completed a responsive grid design that uses two “breakpoints” to create multiple layouts for a page. In this lab, you will continue with this general code structure while adding more layout structure to the page. You will also add support for alternate color schemes, including dark mode.

Starting Code

Start by extracting provided source code and opening the files in your preferred text editor:

For this lab, you will need to modify all CSS and JavaScript files and submit your work to Gradescope. (Do not submit the index.html file.)

Tiny and Print Grids

Throughout this lab, we will refer to multiple breakpoint sizes by the following names:

  • Large screens: width of 1000px or more
  • Medium screens: width of 700px to 999px
  • Small screens: width of 500px to 699px
  • Tiny screens: width of 499px or less

The provided source code provides an overall structure for small and medium (or larger) screens based on the prep. But it’s a jumbled mess on tiny screens. Your first task is to add support for tiny screens. Edit the layout.css to add this support:

  1. Add a new media query for tiny screen sizes
    • Show the header, nav, main, and footer in a column
    • Do not show either of the aside elements
  2. Make the nav list elements into a flex-box to control their layout
    • On tiny and small screens, the list should be in a row with space around the items
    • On medium and larger screens, it should be a column centered horizontally, along with some spacing below each item
  3. Add another media query based on print formatting
    • Show only the header, main, and footer in a column
    • Both header and footer should span the full width
    • Make main occupy one column that gets 90% of the width on the right
    • Do not show the nav or either aside

When you are finished, the tiny layout should have match these top and bottom images. For the print formatting, you’ll need to complete the next section to confirm it is laid out correctly.

Nested Layout

We now have a general structure for the whole page, but now we want to create a structure for the main content. To do this, we’ll create a grid inside a grid. Open cards.css for this part of the lab.

  1. Create a mapping of main elements to grid-areas. You will need separate mappings for the h2 and all six of the articles.
  2. Make the main element into a grid. At this point, if you check the tiny and small layouts, the content becomes a jumbled mess. Create a media query for these layouts to restore the original structure, with the h2 element at the top followed by the six articles in a column.
  3. Create a 2-column layout for medium screens. Both columns should be the same width.
    • You can confirm your medium grid matches the expected version.
  4. Similarly, create a 3-column layout for large screens.
    • Again, confirm your large grid matches the expected version.
  5. If you try to print the page again, you’ll see that making the main into a grid messed up that layout again.
    • Create a print media query to convert the main into a flex-box.
    • Make the articles into a column with space between them vertically.
    • Eliminate the vertical padding inside the h3 and p elements in the articles.
    • Remove the main h2 from the display.
    • Confirm that your print formatting looks correct.

Dark and Stealth Mode

Now that we’ve got the layout, let’s take advantage of a popular feature of modern browsers: dark mode! For this, we’re going to start in dark.css. We’re also going to (intentionally) break accessibility rules to make the text harder to read. Let’s call this “stealth mode”.

  1. Open the index.html and add class="stealth" to the body.
  2. In dark.css, create a new rule for the .stealth class that makes the text use the light color variable.
  3. Get rid of the class="stealth" from the body so we can move on to dark mode.

Dark mode depends on a prefers-color-scheme: dark media query. (You can also make one specifically for light mode.)

  1. Add a dark mode media query that changes the color scheme based on the comments in the file.
    • In your browser, you will need to change your color scheme preferences. The easiest way to test this is to use Firefox. Open the inspector and click on the icon that looks like a crescent moon to turn dark mode on and off.
    • Check your progress again to make sure it looks right.
  2. While we’re at it, add rules for the .stealth class inside the dark mode media query.
    • After adding the class="stealth" back to your index.html body, you can check your progress again.

Event Listeners

So far, we’ve been relying on CSS media queries for everything. For the last part here, let’s use JavaScript to change some things. Open script.js to make the following changes:

  1. Get the #mode-print element and add a click event listener.
    • Calling window.print() will pull up the system dialog to print the page, but…
    • We cannot just put window.print as the event listener’s callback function. Instead, create an arrow function that takes no parameters and calls window.print(). (I.e., () => window.print())
  2. Similarly, add an event listener for the #theme-stealth button.
    • In the body of this function (you can either create a callback function or use another arrow) add/remove the string " (Stealth Mode)" to the header h1 element.
    • Similarly, add/remove the .stealth class to the body.

Going Further

Now that we’re starting to use JavaScript to manipulate the DOM, let’s try one more thing: make stuff disappear.

  1. In script.js, get all of the article h3 elements.
  2. Loop through each article title and add a click event listener.
    • Use an arrow function like before, but this time use a parameter to get the event. You can call it whatever you want, but it’s common to use (e) => { ... } or (ev) => { ... }.
    • In the event model, the thing that you clicked on is the event’s “target”. We can access this using e.target in the arrow function.
    • From the target (the h3 element clicked), traverse the DOM tree to get the p element that immediately follows it. There are a couple ways to do this. You could use parentNode on the h3 to get the parent, then access childNodes to get the appropriate child. You could also just use nextElementSibling on the h3 node to get the sibling.
    • Once you have the p element (you can print it using console.log to make sure), toggle its hidden property.

If you click on the Main and Sixth titles, you should see these results.

7 - Lab 7 - Card Builder

Due: . Make greeting cards to send to your friends (or enemies)!

Greeting Cards

In this (two-part) lab, you will write a tiny app to create greeting cards that you can send to your friends, family, and more! You will need the knowledge you’ve built in the past two preps (forms and localStorage).

For this lab, you will submit the assignment individually (but feel free to collaborate with others).

Getting Started

  1. Download this staring code and unzip it
  2. Open the folder in VS Code
  3. Look over the provided code to get a feel for what it does and where you need to add your own

Creating the Form

We’ve provided the code and styles for the “preview” area of the page, but you’ll need to create a form and provide the functionality for a user to specify what their card’s title, message, to, from, etc. should be. Follow the image and the instructions below:

In card-creator.html:

  1. Create an HTML form inside the form-area
    • Give the form the class card-form
  2. You will need four text inputs and a textarea, each with its own label
    • Put each group of label plus input in its own div
      • Give this div a class of form-group
    • Make sure to include placeholder text
    • You’ll want to make sure each element has a name and id
  3. Add buttons at the end of the form
    • Put them inside a div with the class form-buttons
    • The preview button should have type="button"
    • The save button should have type="submit"
    • Make sure they have ids, so you can access them easier in a script

Adding the Functionality

At the end of the body of card-creator.html it includes a javascript file. Open this file and write JavaScript to do the following:

  1. When the “preview” button is clicked, you should set the text in all the appropriate spans to the value of the corresponding control in the form.
  2. When the “save” button is clicked, you should add the card to an array and save the array of cards to localStorage:
    • You should start by loading the existing array from localStorage with the key cards

      • If there doesn’t exist an entry with the key, create a new array and assign it to a variable
      • If there does exist an entry with the key, you should parse it and assign it to a variable
    • Create a new JavaScript object (aka map/dictionary) to represent the current card

      • It should have five properties: to, from, title, subtitle, and message
      • The properties should be set to the corresponding value from the form
    • Add the new object to the end of the array

    • Store the array in localStorage to the entry with the key cards, overwriting any existing entry

      • Don’t forget: localStorage only lets you store strings, so you’ll need to “stringify” the array!
    • Saving multiple cards should result in an array containing all the cards. You can use the developer console in Chrome/Edge and Firefox to check if you’re doing this correctly:

Greeting Cards, Part 2

In the previous lab, you made a page that lets users create greeting cards and save them to localStorage. But we need a way to view the cards, edit them, and delete them (read, update, and delete in the CRUD operations).

Let’s create a card viewer that displays the data for each card, with a way to edit and delete each one.

Getting Started

  1. Start with your code from the previous lab
    • Let your instructor know if you do not have a working card-creator.html or createcard.js
  2. Here are some more hints for completing the previous lab:
    • localStorage.setItem("cards", ...) will completely replace the existing item in localStorage (meaning you will override the previous array of cards)
    • Thus, you need to make sure to load the existing array of cards with localStorage.getItem("cards"), push a new item to that array, and then save the whole array back to localStorage
    • Don’t forget that localStorage stores values as strings
      • To convert an array or object to a string: var a_string = JSON.stringify(an_object);
      • To convert a string back to an array or object: var an_object = JSON.parse(a_string);

Open up the card-viewer.html and scripts/viewcards.js files. You will be working with these for the lab.

Loading and Looping

First, we will need to load the array of cards from localStorage so that we can loop over each card and display it.

  1. In viewcards.js, use localStorage to get the item with the key "cards" and convert it back to an array. Store this to a variable or constant called cards.

    • Don’t forget to use JSON.parse(...)!
  2. Next, use a “traditional” for loop to loop over the array (using indices – this will be important later!)

    • Also important: use let instead of var for your variables
    • Recommended: save cards[i] to a variable called card to make it easier to reference
  3. Inside the loop, use console.log(...) to print the current card object in the iteration.

  4. Test your page in the browser and open the developer console

    • FYI, you should always be testing your code like this to make sure there are no JavaScript errors!
  5. You should see something like this:

Creating Multiple Cards

The next task you will tackle is to display a card for each item in the array. In the HTML, you will find a <main> element with the id of card-list. All the card displays should be a child of this element.

One way to do this is to create all the elements manually in JavaScript (using document.createElement(...)) to form parts of a card, and then add it to the card list. This, however, is not maintainable. We should be able to define what we want a single card to look like in HTML and CSS and then reuse that code for each card.

The <template> element makes our lives much easier. It lets us write HTML that doesn’t render in the browser, but can be cloned and inserted into our page.

  1. First, take a look at this article on using templates: HTML <template> Tag

    1. Look at the HTML for the examples. Do you see where the templates are defined?
    2. We can use document.getElementById() or document.querySelector() to first select the template
    3. We can then call the .content.cloneNode(true) method on that template, which will return a new Node representing a copy of that template
    4. Then, we can append that copy somewhere on our page
  2. Let’s apply the same principle to our card viewer:

    1. In your JavaScript, select the template element and save it to a variable named template.
      • (You can do this inside or outside the loop)
    2. Inside the loop, call template.content.cloneNode(true) to create a copy of the template, and then save it to a variable named cardView.
    3. Finally, select the element with the card-list class, and call .appendChild(cardView) on it to add the newly created card to the list.
  3. Test your code. You should have multiple cards displayed, one for each card in localStorage:

Displaying the Data

Now, we need to populate the newly created cardView with the data from the current card.

  1. Put your code before you call .appendChild(), but after .cloneNode():

  2. First, select each of the <span>s that are used to display the card’s text and save them to variables:

    • For example, let titleText = cardView.querySelector(".title-text");
    • You should have five of these, one for each span.
  3. Set each of those <span>’s textContent to the appropriate values from the current card

    • Remember that each card is an object, so you can access the attributes directly: e.g., card.title
    • The five attributes are: to, from, title, subtitle, message
  4. Test your code. Each card should display its corresponding text:

Deleting Cards

Let’s make the delete button work. To do so, we’ll need to do a few things.

  1. Continue with your code inside the loop. Make sure that the following lines are still before you call .appendChild()!
  2. We need to make the delete button for the current card do something.
    1. First, select the delete button on the cardView
    2. Then, add an event listener of type "click"
    3. For the callback function, make an inline function – this is important!
  3. When the button is clicked, we want to delete this card. The problem is, how does each delete button know which card its referring to?
    1. In the callback function, write console.log(i) to log the current index.
    2. Test your page and open the developer console. Click on the delete buttons. Notice how each button correctly prints out the corresponding index!
      • How does this work? When we create a function, JavaScript will “save” the state of our variables in something called a closure.
      • If this isn’t working, you may have declared your variables with var instead of let. This is one of the advantages of let. It scopes the variable to the block instead of the function, which lets us keep its current state.
      • If closures scare you, there is an alternative method below
  4. Now, in the button’s callback function:
    1. Make the button delete the item in the array at the current index
    2. Afterwards, save the array of cards back to localStorage
      • Don’t forget to stringify it!
    3. Reload the page by calling location.reload()
      • We do this because, after deleting an item, the indices in the array are no longer valid, so let’s refresh the page and display all the cards again
  5. Test your code. You should be able to delete a card and, once the page reloads, it should no longer be displayed.

(If you have time) Making the Cards Editable

Last but not least, let’s make the cards editable. You may have noticed that there’s a contenteditable attribute on each of the <span>s! This makes the element into something that works both as text and an input.

Elements with contenteditable have an event called "input", which is fired whenever a user changes the value of the element.

  1. Inside the loop, but still appendChild, let’s create a new function. Yes, right here.

    1. Name the function updateCard()
    2. Inside, you should set each of the current card object’s attributes (to, from, title, subtitle, message) to the content from the corresponding <span>s
      • For instance, card.title = titleText.textContent; or cards[i].title = ...
    3. Afterwards, save the entire array to localStorage again.
      • Don’t forget to stringify it!
  2. After the function definition, but still before appendChild, add an event listener for the "input" event to all five <span>s. Use your updateCard function as the callback:

    • E.g., titleText.addEventListener("input", updateCard);
  3. Test your code by clicking in one of the fields and editing it. You should see the value change in localStorage:

    • If you are having issues, double-check:
      • Make sure you are using either let or const for your variables/constants, not var
      • Make sure you are passing updateCard as an argument, and not accidentally calling it with updateCard()

Alternative to Using Closures

Instead of using closures, you can also “store” the index of each card in the HTML elements themselves! We can create a custom HTML attribute, called a data attribute.

Here’s how that would work instead of closures:

  1. On each card view and/or each of the <span>s:
    1. Add a new data attribute called index using JavaScript (aka "data-index" in HTML)
    2. Set the value to the current index of the card
  2. In your event handlers/callback functions, retrieve the index attribute.
    1. Attributes are stored as strings, so you may need to convert it back to an int
    2. Use this value to specify which card to delete/update

Submission

Submit your code to gradescope.

8 - Lab 8 - Play Fetch

Due: . Use fetch() to interact with third-party APIs and modify HTML based on the result.

Learning Objectives

By the end of this lab, you should be able to:

  1. Understand the different request methods and how they differ
  2. Use fetch() to interact with third-party APIs
  3. Modify an HTML page based on the result of an API call

Introduction

In this lab, you will use the knowledge you’ve learned about HTTP requests, asynchronous Javascript (including Promise), and fetch() to interact with a third-party API and use its results to modify an HTML page.

You’ll start by practicing your requests and learning about the different request methods and the different ways of formatting data for a request.

Then, you’ll retrieve data from an API and use it to populate a page and modify the DOM.

In the last part, you will write some starter code that interacts with the specific API you chose for the class project.

Getting Started

Start by going to the GitHub Classroom link provided by your instructor and accept the assignment. For this lab, you may work in your project groups.

Clone the created repository to your computer. You will need to use Live Preview or Live Server for this lab; opening the file directly in your browser may prevent some requests from working.

Open the repository folder in VS Code.

Part 1: Requests Practice – Echo (echo) Server

For this first part, we will be interacting with an “echo server” that simply echoes back the request that you give it. We’ll use Zuplo’s Echo API.

Start with the part1.html and js/part1.js files. You will see a couple of input fields and three buttons, plus an area for the output. Each of the buttons needs to make a specific request to the echo API, and then display the resulting JSON in the output. You shouldn’t need to modify the HTML for this lab, just reference it. Edit the JavaScript

  1. Write the event handler for get-btn (this should be stubbed out for you in part1.js):

    1. This function should send a GET request to the https://echo.zuplo.io/api endpoint.

    2. The request should include the name and the age as parameters in the URL’s query string

    3. There are two ways of accomplishing this:

      • Manually add the keys and values to the URL:
        let resp = await fetch("https://www.mywebsite.com/search?q=" + query + "&page=" + pageNum);
        
      • Use URLSearchParams and give the constructor an object containing the keys and values you want to encode:
        let params = new URLSearchParams( {q: query, page: pageNum} );
        let resp = await fetch("https://www.mywebsite.com/search?" + params.toString());
        
        (Note: the URLSearchParams constructor can also parse a query string, and then you can easily access its keys/values)
    4. After you get the response from the server, parse the body as JSON

    5. Set the textContent of the <pre> element (with the id of output) to a nicely-formatted version of that JSON:

      • JSON.stringify(...) can take in three parameters: the object you want to stringify, a “replacer” (which can be null), and the number of spaces to indent the JSON. This will produce a formatted version of the data:
        let data = {
            city: "Seattle",
            population: 757992,
        };
        let formatted = JSON.stringify(data, null, 2);
        // JSON.stringify(object_to_format, null, num_spaces_for_indent)
        
    6. Test the “GET” button (open the developer console to check for errors). You should see something like this:

      GET example output

      Notice how the "query" attribute contains the parameters from the query string we gave.

  2. Next, write the event handler for post-json-btn:

    1. This function should do the same thing, except, instead of a GET request, it should send a POST request.

    2. The body of the request should be JSON data that contains the name and the age:

      • The fetch() function can accept a second argument, which is an object that specifies the request options:
        let options = {
            method: "POST",
            headers: {
                "Content-Type": "application/json", // or "application/x-www-form-urlencoded", "text/plain", etc.
            },
            body: JSON.stringify( {q: query, page: pageNum} ),
        };
        let resp = await fetch("https://www.mywebsite.com/search", options);
        
    3. Again, after you get the response, parse the body as JSON and display it in output. This should look like:

      Note how now the request’s data was found in "body" instead of "query".

  3. Finally, write the event handler for post-form-btn:

    1. This function does the same as the last, but, instead of the data being formatted as JSON, the data should be formatted as “URL-encoded” form data. This is how HTML forms usually POST their data to a server.

    2. The body of the request basically contains data in the same format as a query string:

      let options = {
          method: "POST",
          headers: {
              "Content-Type": "application/x-www-form-urlencoded",
          },
          body: new URLSearchParams( {q: query, page: pageNum} ),
      };
      let resp = await fetch("https://www.mywebsite.com/search", options);
      
    3. You should also parse the response’s body as JSON and display it in output. This should look like:

      Now, look at how the request’s data are formatted in the "body".

With Part 1, you should feel comfortable using fetch() to make requests and include data in both the query string and the request body. These are fundamental tools to add to your arsenal for dealing with any API you may encounter!

Part 2: List Those Users!

Now that we’ve practiced making API requests with a simple echo server, let’s go a little further and interact with an API that actually gives real (fake) data! We will use reqres.in, which provides mock data via a simple set of API endpoints.

Open the part2.html and js/part2.js files. The HTML contains an example of how a user should be displayed and an empty <div> with the id of user-list.

  1. In js/part2.js, inside the DOMContentLoaded event handler, write code to request the first page of users from reqres.in:
    • First, take a look at the API endpoints for reqres.in, specifically the “List Users” endpoint.
    • The base URL is "https://reqres.in", and the endpoint is "/api/users". This means that the full URL you should send the request to is "https://reqres.in/api/users".
    • What request method does it use? (GET? POST? PUT?)
    • How do you specify which page to retrieve? (Query string? JSON body? Form body?)
  2. Once you get the response from the API, you should parse the body as a JSON object.
    • We don’t always do this; it depends on what the API endpoint returns! (aka we need to read the documentation)
    • For reqres.in, all the endpoints return stringified JSON data in the response body.
  3. Next, you’ll want to loop over the returned list of users and create new elements in the HTML for each user.
    • Take a look at the format of the returned data first on the reqres.in website!
    • Each new user added to the HTML should follow this format:
      <div class="card">
          <h2>Firstname Lastname</h2>
          <img src="https://reqres.in/img/faces/some_image.jpg" alt="Firstname Lastname">
      </div>
      
    • You can create the elements manually in JavaScript, you can clone the existing example, or you can create a <template> to use.
      • You may minimally modify the HTML to help you, but the format of each user must match the example above.
    • Make sure each new user card is added to the <div> with the id of user-list. Also don’t forget to add the "card" class to the <div>!

The result should look like this: Part 2 Finished

Part 3: Your Project APIs

For this last part, you will interact with the two APIs you chose for your semester-long project. For now, you will simply make a request and display the response.

Open the part3.html and js/part3.js files. You will need to modify both the HTML and JS for this part.

  1. In part3.html, for API #1, replace [TODO] with the name of one of your APIs.
  2. Next, go to the documentation for your specified API. Find a specific endpoint you want to use. Take note of the following:
    • What is the base URL for the API?
      • (This is usually found in a “Getting Started” or “API Concepts” documentation)
    • Does your API require authentication?
      • (This is also usually found in “Getting Started” pages)
    • What is the path to the specific endpoint?
      • (Found on your specific endpoint’s documentation)
    • What method does the endpoint use?
      • (GET, POST, PUT, PATCH, DELETE)
    • How is the data specified?
      • (In the query string? In the body as JSON/form-urlencoded?)
    • What format is the data returned in?
      • (JSON? Text? No data?)
  3. In the HTML, replace the [TODO] for the URL with the full URL of the endpoint you will be using.
  4. Also replace the method ([GET/POST/PUT/...]) with just the specific one you will be using.
  5. In the js/part3.js file, in the event handler for the api-1-btn, write code to make the request and output the returned data.
    • If your API’s response does not contain any data in the body, output the status code instead.
    • I recommend using the MDN docs on fetch() as a reference.
    • Also console.log(...) the response object (from fetch()) so you can look at it in the developer console!
    • Make the output nicely formatted if it’s JSON, just like in Part 1.
  6. Test the button. Does it display anything? Make sure to open the developer console to check for errors!
    • Usually the HTTP status code of the response can help diagnose what’s wrong.
      • Sometimes, its as simple as missing a parameter, or forgetting to include the “Content-Type”!
    • If you are getting an error saying something about “CORS” or “origin”:
      • Does your API support CORS?
        • (This can be found in the documentation or on the public api list if that’s where you found your API)
      • What is CORS?
        • Cross-Origin Resource Sharing (CORS) is a way for a server to say, “Hey, I’m allowing other webpages to access me! Here’s a list of the ones I approve of.”
        • Without CORS enabled, web browsers will NOT allow a page to access that specific server/resource! This is for security reasons; otherwise, malicious websites could send requests to, say, your banking site, using your login credentials!
        • However, server-side applications can still access that server; CORS is only for restricting client-side access.
        • This means that we are unable to access it from anything we do in CS 343. We would need to access it via a backend.
      • But wait! There’s a workaround:
        • Simply tell a server to make the request for you!
        • There’s a free “CORS Proxy” at corsproxy.io you can use. There is also allOrigins.win as an alternative.
        • The CORS proxy will act as a intermediary between you and the API server. This does open up some concerns about latency and security, so only use it for non-sensitive data and only for this class.
    • Did your API return something about “unauthorized”?
      • You may need to authenticate your app! Look in the documentation to see how you need to retrieve an “API key” or “access token” of some kind.
        • This is usually then given to the API in the header of your request, or even in the query string.
        • There are other methods of authorization, which may be more complicated to use.
      • Are you using the Spotify API? It’s a little more complicated to authorize!
        • We’ve provided a tutorial here and another demo here.
  7. Once the first API is working, repeat the steps for the second API!
  8. This should give you a head start on the last part of the project.
    • For your actual project, you will need to do something with the response, not just output it!

Submission

Commit and push your code back to GitHub.

9 - Lab 9 - Later Gator

Due: . Asynchplorations in ronExicity.

10 - Lab 10 - Persistence: Import/Export

Due: .
  1. Build on earlier experience with the browser’s localStorage and location Web APIs to create a more featureful user experience.
  2. Build on earlier experience with the browser’s localStorage using the FileReader to support export/import of app state.

Part 1: Persistodon 🦕

Learning Objectives

By the end of this lab, you should be able to:

  1. create (receive/parse) a “sharable link” such that another user of your app would get to a particular state of the app
  2. export from– and import to localStorage

Prerequisites

  1. beginning localStorage (e.g. as exercised in Lab 7 - Card Builder)
  2. Familiarity with the browser’s Location interface (e.g. as outlined in prep 10)

Instructions

  1. Begin with the starting code in the Persistodon repo
  2. Complete the Persistodon app as specified in the page’s aside.

Part 2: Transportodon 🦖

Learning Objectives

By the end of this lab, you should be able to:

  1. export from– and import to– localStorage

Prerequisites

  1. beginning localStorage (e.g. as exercised in Lab 7 - Card Builder)
  2. Familiarity with the browser’s FileReader API

Instructions

  1. Begin with the starting code provided by your instructor (e.g. from an assignment in your learning management system) or else in the Transportodon repo
  2. Complete the Transportodon app as specified in the page’s aside.

11 - Lab 11 - Information Visualization

Due: .

12 - Lab 12 - Security

Due: .