CSE 154

Section 6: Exploring DOM Manipulation

Agenda

  1. Review accessing and manipulating the DOM with JS
  2. JS practice with DOM and events: Tier List Maker!

QuickCheck

Grab a notecard and answer the following questions:

  1. On the front, give feedback to your TA by answering these questions:
    • What should they start doing?
    • What should they stop doing?
    • What should they keep doing?
  2. On the back, If you were to run into a bug, what would be your first step to debug it?

CP2 and HW2

  • CP2 is due Wednesday, October 16th @ 11pm (Friday lock date)
  • HW2 Part A due Saturday, October 19th @ 11pm
    • No late days accepted. Due date is the lock date.
  • HW2 Part B due Wednesday, October 23rd @ 11pm (Friday lock date)
  • Part A of HW2 is NOT a half way point of the assignment.
  • DO NOT wait to work on Part B until after Saturday.
  • Go to the WPL early. This assignment is more challenging and office hours get busy.

Creating and Appending New Nodes to the DOM Tree

Name Description
document.createElement("tag") creates and returns a new empty DOM node representing an element of that type
parent.appendChild(child) Appends the given DOM element (child) into the parent DOM element (parent).
// create a new <h2> node
let newHeading = document.createElement("h2");
// Add text to the node
newHeading.textContent = "This is a heading!"
// append the h2 to element with id #intro
id("intro").appendChild(newHeading);

JS

Form Element Overview

The slides below describe some of the different HTML elements found in the section exercises, as well as some extra ones, which you may find helpful for following the JS requirements (and learn a bit more about common types of UI elements)!

Form Element Overview: Grouping UI Elements

It is often useful to group form-elements together on the page to show that they are related.

  • fieldset: A container to group UI elements
  • legend: A legend for a fieldset which appears by default as the text within the top-left border

Note that we also see form elements used to group UI elements, but since we're not submitting to any server in this exercise, we choose to use a fieldset.

Form Element Overview: User input with textarea

textarea elements can be used for multiline text input, such as paragraphs or messages. These are used in the extra Encrypt-It exercise.

Form Element Overview: Dropdown Menus

select elements represent dropdown menus. They contain option elements, which represent each option in the dropdown.

The value of the select element is equal to the value of the chosen option element.

These are used to select tiers in the tier list maker, and you will be programatically populating the options of the select to match the tiers.

Form Element Overview: Input Element

The input element serves many roles, depending on its type attribute.

  • type="text": Accepts single-line text input.
  • type="radio": Accepts one of multiple options. Give multiple radio inputs the same name attribute to link them together.
  • type="checkbox": Like radio, but accepts any number of the given options.

There are many more that you can look up on MDN. The tier list maker uses text input elements.

Form Element Overview: Labels

Oftentimes you want to label form elements with text, and have the form element get selected if the user clicks on the text. By surrounding the text with a label element, and giving it a for attribute equal to the id of another form element, it will select that form element remotely when clicked.

Screenshot of the tier list maker.

This is a pretty complex page! Here is a running solution (don't read the code). We will take it step by step. Be sure to test your code as you go. Go down to get started.

Tier List Maker Overview

These exercises are designed to get you acquainted with creating and appending elements on the page, interacting with form elements, and manipulating the page in other ways through event-driven programming. When you are done, you will have a functional tier-list maker, which is a tool designed to show the relative value of various related things (and quite popular on the internet lately.)

We have provided tier-list.zip with starting HTML for you to use and build upon, as well as starter CSS which you are free to edit. There is also a mostly-empty JS file to follow along in.

Feature Overview

We will be implementing the following features, in this order:

  1. Event-listeners for UI elements
  2. Toggling between views with buttons
  3. Populating text input elements equal to number of tiers
  4. Creating the rows of the tier list
  5. Changing the dropdown form to match the names of the tiers
  6. Adding items to the tier list
  7. Selecting items on the tier list
  8. Removing items from the tier list

There are many further changes you can make beyond this if you want to make a robust, powerful tier-list tool, but this is a good starting point.

Step 1: Event Listeners

The provided JS code has many empty functions to help guide your programming. In practice, you will have to determine for yourself what functions need to be built to make a product, which takes practice.

Hook up event listeners for four UI elements to these functions to get started. You can use alerts to confirm they are set up properly. Read the function documentation to determine which function matches the intended goal.

  • #make-list: On click, should switch to the maker view and generate the empty tier list in place of whatever was there before.
  • #row-count: On change, update the number of text input elements to match the number of tiers.
  • #add-item: On click, should add an item to the tier list based on user input.
  • #go-back: On click, switch to the setup view.

Step 2: Toggle Views

Add code to the relevant functions you linked in part 1 so that clicking the #make-list element goes to the #maker view, and clicking the #go-back element goes to the #setup view.

You may find classList.toggle(class) to be a useful DOM object function. It will flip the class on/off to the opposite state of what it was previously in

Removing Elements

You can remove DOM elements from the page in a few different ways.

  • You can select the element to remove and do element.parentNode.removeChild(element);. This gets the elements parent, and tells the parent to remove itself as a child.
  • If you want to clear all of the elements out of a container, you can do so with container.innerHTML = "". This will clear out all of the HTML inside.

NOTE: For code quality, do not use innerHTML outside of clearing parent containers in this class. This is due to security vulnerabilities with innerHTML as well as other code quality concerns.

Step 3: Populate the text input elements.

When the #row-count element is changed by the user, clear out the text input elements previously in the #row-names element, and repopulate it with the number of elements specified in the #row-count element (do not worry about negatives.)

When making an input element with createElement, you need to remember to set the type attribute on the element to the correct kind.

Step 4: Creating the tier list rows

Each tier-row has the following structure:

<div class="tier-row">
  <p class="tier-name"><!-- Tier Name Here --></p>
  <div class="tier-items" id="tier-0">
    <!-- img elements here later -->
  </div>
</div>

HTML Reference

Start by writing generateTierRow to generate and return this HTML object. The tier name and .tier-items id are passed in as parameters.

Next, using generateTierRow, write some of makeList that will append a .tier-row to the #tier-list for each row specified in the #setup text inputs. Use "tier-n", where n is the row from the top, as an id to reference later when we add items to the list. Be sure to clear out whatever tier list was there previously before you start adding rows, and add the relevant classes so that the CSS works.

Step 5: Creating the dropdown menu

Also in makeList, we will be creating option elements for the #tier-select element, one for each tier. Each option should have text content equal to the tier name, and a value equal to the id you set in step 4 on the .tier-items element. This way, a selected tier name can link to a row in the list. To factor out some code, you might want to also write and use the generateTierOption function.

Step 6: Adding items to the tier list

Finally we can add some items to the list. Complete the relevant functions so that when the #add-item button is pressed, an item is added to the list in the specified tier, with the specified src, and with an alt equal to the item name.

Step 7: Selecting items

If the user wants to view the name or image source of a tier item, they should be able to do so by clicking the item. When a tier-item is created, add a click event listener to it which triggers a function that displays the item's properties in the fieldset. Then complete that function.

Step 8: Deleting items

Finally, we want to be able to delete items. Add another event listener to each item, this time for double-clicks, which deletes the item from the DOM tree.

With steps 7 and 8 implemented, you can now edit items by double-clicking to remove them, changing some of the values in the fieldset, and then re-adding them!

(Challenging!) ideas for extending the website

These are non-testable things you could look into if you are curious how they might be done.

  • Instead of adding to rows with a dropdown, what if we added to a buffer row, and then were able to use drag/drop to move the items around?
  • It would be good if there was a better way to save the state of the list than with a screenshot. Later in the course we will cover tools like SQL and localstorage for accomplishing this.
  • It would be cool if hovering over the items displayed their name somewhere on the page.
  • It would be nice if we could upload local images to use in the tier list, such as with a file input element
  • A convenient feature would be the ability to add, remove, and rename the tier-list rows while in the maker view with new UI elements. This would make the tool a lot easier to use.

Additional Practice: Encrypt-It!

Encrypt-It solution output

Encrypt-It Overview

In this series of exercises, you will be practicing using different UI elements in HTML with a JavaScript program to perform some operations on user input. Specifically, you will finish a cryptogram generator, which takes any message as input and outputs a cipher message in a few different possible outputs.

We have provided encrypt-it.zip with starting HTML for you to use and build upon. We have also included some CSS, although you're free to add styling on your own!

Part I: Write the start of encrypt-it.js

Now you'll write a bit of JavaScript testing code that logs a message to the console. This is just a test to make sure that your browser is running your JavaScript file, before we move on to the main execises.

  • The starter zip file includes a starter JS file, which uses the module-pattern introduced this week.
  • Put the following line of code into the file: console.log("Window loaded!"); such that the message is logged to the console when the page is loaded
  • Link your HTML page to your JavaScript file using a script tag
  • Refresh your page in the browser. Do you see the console message? If so, move on. Otherwise, double-check your script tag syntax or ask a TA for help.

Part II: Testing a Button

Now let's set up a very basic JS event handler. Modify your JS code and so that the "Button clicked!" console message won't output until the user clicks the "Encrypt-It!" button.

Strategy:

  • First, make sure you understand why the event listener for window's load event is needed (remeber we don't have access to the DOM until the page is loaded).
  • Move your console statement inside a new function handleClick.
  • Add an event listener to the the "Encrypt-It!" button such that when clicked, your handleClick function is called.
  • Refresh your page in the browser. Click the button. Do you see the console.log message? If so, move on. Otherwise, double-check the syntax and for both of your event listeners (window load and button click), or ask a TA for help

Part III: Implementing a Basic Shift-Cipher

Modify your JS code so that when the user clicks "Encrypt-It!", the text in the input text area will be encrypted using a basic shift-cipher, and output into the page's paragraph element with the id of output.

Details:

  • To get text from the textarea, you'll need to make sure you can access it from JS. Remember you can use document.getElementById or document.querySelector to access a DOM element in JS.
  • Modify (and appropriately rename) your handleClick function so that when called, it now retrieves the textarea's text value and generates a shift cipher (algorithm discussed on the slide below; a solution for this cipher function is provided at the very bottom if you'd like to skip the algorithm part). This generated cipher will be output as text in the #result paragraph.

Part III: The Shift-Cipher Algorithm

The rules of a shift cipher are fairly straightforward. Let the English alphabet we all know and love(?) be called A. Let the shift-encrypted alphabet be called C. For simplicity, we will shift letters in our encryption function by 1 letter. Then C is defined as mapping each letters in A to the letter alphabetically next. For example, 'a' is mapped to 'b', 'b' is mapped to 'c', ... and 'z' is mapped to 'a' (creating a cycle of 26 letters). In this exercise, we will consider uppercase letters and lowercase letters equivalent to one another (ie, 'a' is considered equal to 'A').

Visually, the cipher can be represented as the following:

input letter     a b c d e f g h i j k l m n o p q r s t u v w x y z
                 | | | | | | | | | | | | | | | | | | | | | | | | | |
                 v v v v v v v v v v v v v v v v v v v v v v v v v v
output letter    b c d e f g h i j k l m n o p q r s t u v w x y z a
            

Your task in this part is to convert the text in the input text area from alphabet A to alphabet C. This is all you need to know to implement the cipher in this lab, but if you would like additional hints, there are some provided in the slide below.

Part III: Hints

Note that the value you get from the textarea is just a long string. So your goal is to build up a new string that is the result of applying the cipher to each letter in the input text, in order, and adding it to your result string.

There are a few ways to go about this, but note that one of the most intuitive approaches would be to use a for loop through the input string and add 1 to each letter (letters are actually represented by numerical values, so this is a natural operation). To handle the z -> a shift, you can add a special case for each letter, or use mod arithmetic to avoid this extra case.

You may find charCodeAt(index) and fromCharCode(asciiNum) helpful for this problem.

If you get stuck on this function, you may refer to a sample solution on the slide below. But it's strongly recommended you implement it on your own!

Part III: Algorithm Solution

One function solution is given below (you can also solve this with arrays):

/**
 * Returns an encrypted version of the given text, where
 * each letter is shifted alphabetically ahead by 1 letter,
 * and 'z' is shifted to 'a' (creating an alphabetical cycle).
 */
function shiftCipher(text) {
  text = text.toLowerCase();
  let result = "";
  for (let i = 0; i < text.length; i++) {
    if (text[i] < 'a' || text[i] > 'z') {
      result += text[i];
    } else if (text[i] === 'z') {
      result += 'a';
    } else { // letter is between 'a' and 'y'
      let letter = text.charCodeAt(i);
      let resultLetter = String.fromCharCode(letter + 1);
      result += resultLetter;
    }
  }
  return result;
}

JS

Part IV: Implement UI Functionality

Now that you have a cool encryption feature implemented, let's add some features for the user to decorate their encrypted message.

We have broken this part into are a few smaller exercises - move down the slides to work through them!

Part IV: Dropdown UI

First, we want to restrict the shift cipher functionality to only work when "Shift Cipher" is selected in the drop down menu. Only when this is the currently-selected option and then the "Encrypt-It" button is clicked should the input text be encrypted.

For dropdown menus, you can get the value of the current option using el.value, where el is the DOM element for the select dropdown.

Part IV: Reset Functionality

When the Reset button is clicked, the textarea and the output paragraph should both be cleared (using textContent).

Part IV: Checkbox Elements

Next, implement a feature such that when the all-caps checkbox is checked, all of the text in the output paragraph appear only in uppercase.

To do so, add a CSS file with a class ".uppercase" that uses text-transform: "uppercase"; to make an element all-uppercase. When the "All Caps" option is checked, the output paragraph should have this class. When unchecked, it should not have this class so that it uses whatever casing was used in the original input text.

Hint 1: The event corresponding to changing the state of a checkbox input as checked/unchecked is change.

Hint 2: To update the class list of a DOM element, you can use el.classList.add("classname") and el.classList.remove("classname"). We'll see this more in lecture!

Part V: Radio Button Elements

Implement a feature to such that the text in the output paragraph should have the font size of whatever is currently selected in the radio button group.

To minimize changing styles in JavaScript, you can add CSS classes to accomplish font size changes (similar to the previous step).

Part V: Implementing the Randomized Cipher

Note: This part is a more challenging feature we have provided if you'd like to explore a more advanced cipher algorithm and and add a few more UI features. We have also suggested other features to try on the next slide!

In this part, you will implement the "Randomized" cipher option. This is similar to the Shift Cipher, only "result alphabet" C is randomly-generated. In other words, each letter in the English alphabet is mapped to one of the 25 other letters, but no two letters may have the same mapping (e.g., "a" and "c" can not both map to "d").

Part V: Details

First, you should modify your button event handler to determine whether the "Shift Cipher" or "Randomized Cipher" option is selected in the dropdown menu. If the "Randomized Cipher" option is selected, it should call a new function which outputs a randomized encrypted version of the input text area to the output div.

If you would like some hints for writing this algorithm, there are some in the slides below.

Part V: Hints

Because we aren't simply adding 1 to each letter (or mapping 'a' to 'z'), you might find an array helpful when creating a random cipher alphabet. One way to randomly-generate the cipher is to keep track of unchosen letters in the English alphabet using a string or array. Then, randomly choose one of the unchosen letters (you may find Math.floor() and Math.random() useful here).

Next, push this randomly-chosen letter to the end of the cipher array. Continue this process until you have removed all letters from your English alphabet and have 26 letters in your cipher array (where the first index is the letter randomly-chosen first, and the last index is the letter that was chosen last).

If you get stuck on this function, you may refer to a sample solution on the slide below. But it's strongly recommended you implement it on your own.

Part V: Algorithm Solution

One possible solution is given below:

function generateCipher(text) {
  let alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
  let cipher = [];
  // it's poor style to hardcode a magic number like 26
  let alphabetLength = alphabet.length;
  for (let i = 0; i < alphabetLength; i++) {
    let randomIndex = Math.floor(Math.random() * alphabet.length);
    cipher.push(alphabet.splice(
      [Math.floor(Math.random() * alphabet.length)], 1));
  }
  document.getElementById("output").textContent = cipher;
  let result = "";
  for (let i = 0; i < outputText.length; i++) {
    if (text[i] >= 'a' && text[i] <= 'z') {
      let letterCode = text.charCodeAt(i) - 'a'.charCodeAt(0);
      result += cipher[letterCode];
    } else {
      result += text[i];
    }
  }
  document.getElementById("output").textContent = result.replace(",", "");
}

JS

Challenge Problems

Did you enjoy making your own encrypted messages? Here are a few other ideas to add to your file:

  • Add a dropdown menu to select a value to shift by in the Shift Cipher
  • Adjust your ciphers so that digits 0-9 can be encrypted as well as letters
  • Explore other cipher types
  • Change your cipher to output something other than letters): For example, you could output using emojis, gifs, colored boxes, etc.
  • Write a "Decryption" tool! This goes beyond the scope of this exercise, but with a .txt file of dictionary words, you can try to write a tool that takes input (probably from a Shift or Randomized cipher) and tries to decode it, matching sequences of characters to words in the dictionary.

Full Encrypt-It Solution

Encrypt-It Solution