CSE 154

Lecture 11: Deeper JS Language Features

AKA: How to avoid common errors

Quick Administrivia

What we've covered in Module 2 so far...

  • Basic JavaScript
    • Types (Strings, Numbers, Arrays)
    • Conditionals, Loops, Functions
  • Basic HTML UI Elements (buttons, text input, etc.)
  • Handling Events
  • Page Event Flow
  • Modular JS Pattern and Scoping
  • Timed events (Delays/Intervals)

What's left in Module 2

More!!!

  • More on JavaScript types, including null and undefined
  • More on (resetting) timers
  • More on arrow functions
  • More on (computed) styles
  • More on (programatic) DOM tree traversal and TextNodes
  • More on JavaScript types

    including null and undefined

    Reminder: "Types" in JavaScript

    Types are not specified, but JS does have types ("loosely-typed")

    • Number, Boolean, String, Array, Object, Function, Null, Undefined
    • Can use typeof to find a variable's type but it is poor practice.
    let level = 23; // Number
    let accuracyRate = 0.99; // Number
    let name = "Pikachu"; // String
    let temps = [55, 60, 57.5]; // Array
    

    JS (example)

    Boolean Type

                let iLikeJS = true;
    if ("web dev is great") { /* true */ }
    if (0) { /* false */ }
    if (1) { /* true */ }

    JS

    Any value can be used as a Boolean

    • "falsey" values: false, 0, NaN, "", null, and undefined
    • "truthy" values: anything else

    Understanding what is "falsey" vs. "truthy" takes patience and practice.

    When in doubt, check in the browser console!

    Logical Operators

    Relational: > < >= <=

    Logical: && || !

    Equality: == !=

    Strict Equality: === !== (checks both type and value).

    • Most logical operators automatically convert types.
      • 5 < "7" // true
      • 42 == 42.0 // true
      • "5.0" == 5 // true
      • "5.0" === 5 // false
    • It is usually a good idea to use === instead of ==.

    A good reading on == vs. === (and don't forget about the JS equality table)!

    Common error with type coercion

    let woops = "abc" > 0; // false

    JS

    But why??!??!

    parseInt("abc") === NaN and NaN > 0 is false

    Why is this important?

    Imagine you want to validate user input on your page to ensure someone has typed a non-blank value into an input text box (id #my-input) before hitting the submit button... so you add this to your .js file

    if (id("my-input").value > 0) { /* do submission stuff */ }

    But there's a bug! What is it?

    The fix

    if (id("my-input").value.length > 0) { /* do submission stuff */ }

    Special Values: null and undefined

    let foo = null;
    let bar = 9;
    let baz;
    
    /* At this point in the code,
     * foo is null
     * bar is 9
     * baz is undefined
     */

    JS

    undefined: declared but has not yet been assigned a value

    null: exists, but was specifically assigned an empty value or null. Expresses intentional a lack of identification.

    A good motivating overview of null vs. undefined

    Note: This takes some time to get used to, and remember this slide if you get confused later.

    Common Error...

    Did you see this in the inspector when working on CP2 or HW 2?

    The cause is typically that you tried to dereference a DOM element was null because the id does not exist.

    <button id="bar-btn">Submit</button>

    HTML

    id("foo-btn").addEventListener("click", handleButton);

    JS

    Only non-null objects can be dereferenced using the . (dot) notation to call a method or retrieve a value stored in that object

    A Few Last Notes on Types in JavaScript

    As you write JS programs, you may will run into some silent bugs resulting from odd typing behavior in JS. Automatic type conversion, or coersion, is a common, often perplexing, source of JS bugs (even for experienced JS programmers).

    Why does it happen? JS was designed to "work" as intuitively as possible without requiring the strict types.

    Why is this important to be aware of? You'll be writing programs which use variables and conditional logic. Knowing what is considered truthy/false and how types are evaluated (at a high level) can make you a much happier JS developer (some practice)

    Examples of some "less-intuitive" evaluations:

    154 === 154.0// true

    2 < 1 < 2;// true

    0 + "1" + 2;// "012"

    0.1 + 0.2 == 0.3;// false

    [] + [];// ""

    "1" / null;// Infinity

    [0] === true// false (expected?)

    !![0]// true

    This is worth 3 minutes of your viewing pleasure. (starting at 1:20)

    More on (resetting) timers

    With Bubbles!

    Toggling Timers

    <button id="toggle-btn">Start/Stop<button>
    

    HTML

    let timerId = null; // stores ID of interval timer
    function initialize() {
      id("toggle-btn").addEventListener("click", toggleMessageInterval);
    }
    
    function toggleMessageInterval() {
      if (!timerId) {
        timerId = setInterval(sayHello, 1000);
      } else {
        clearInterval(timerId);
        timerId = null;
      }
    }
    
    function sayHello() {
      id("output-text").innerText += "Hello!";
    }

    JS

    output (full example page)

    Common error with timers

    Common error: Not resetting the timer to null after clearInterval!

    Your timer may not start again or you may wind up with a proliferation of timers

    let timerId = null; // stores ID of interval timer
    ...
    
    function toggleMessageInterval() {
      if (!timerId) {
        timerId = setInterval(sayHello, 1000);
      } else {
        clearInterval(timerId); // Do this first (don't forget)!!!
        timerId = null; // Don't forget this!!!
      }
    }

    JS

    What happens if you swap the clearInterval(timerId)/timerId = null?

    Do you always need a timerId? (think about this on your own)

    More on Fat Arrow Functions

    Fat arrow functions

    Fat arrow functions are just another way of writing an anonymous function.

    /* named function with one parameter that logs to the console. */
    function sayHello(you) {
      console.log("Hello " + you);
    }
    
    /* Equivalent function as an anonymous function */
    (you) => {
      console.log("Hello " + you);
    }
    
    /* Equivalent function with no parens because there is only 1 parameter */
    you => {
      console.log("Hello " + you);
    }
    
    /* anonymous function with no parameters */
    () => {
      console.log("Hello!");
    }

    JS

    Fat arrow function syntax

    Some sample basic syntax (from MDN)

    // General form with multiple parameters
    (param1, param2, …, paramN) => { statements }
    
    // Multiple parameters and a return statement
    (param1, param2, …, paramN) => { return expression; }
    // or ...
    (param1, param2, …, paramN) => expression
    
    // Parentheses are optional when there's only one parameter name:
    (singleParam) => { statements }
    singleParam => { statements }
    
    // The parameter list for a function with no parameters should
    // be written with a pair of parentheses.
    () => { statements }

    JS (templates)

    Note: the scoping of this is different with fat arrow functions that traditional anonymous functions ()

    Examples

    // Quick max and min function definitions
    let max = (a, b) => a > b ? a : b;
    let min = (a, b) => a < b ? a : b;
    
    // Array filtering
    let arr = [1, 2, -1, 0, 3, 4, 6, 20];
    let even = arr.filter(v => v % 2 == 0);    // [2, 0, 4, 6, 20]

    JS

    More information on filter

    Fat arrow functions in CSE 154

    • This information on the "fat arrow" notation was mostly "FYI" because you are going to run in to them in documentation.
    • You may continue to use anonymous functions as taught previously (function() { }) or use fat arrow functions as you feel comfortable with them. You do NOT have to use fat arrow functions at all.
    • As always, we would prefer you be consistent in your usage of either regular anonymous functions or fat arrow functions as that is good Code Quality
    • Remember to only use anonymous functions (in general) when you have a small number of statements (<= 3 ish) that won't need to be reused or duplicated anywhere.

    More on reading & changing styles

    Review: Changing styles

    Recall that the .style property of a DOM object lets you set any CSS style for an element

    button { font-size: 16pt; }

    CSS

    <button id="clickme">Click Me</button>

    HTML

    window.addEventListener("load", initialize);
    function initialize() {
      document.getElementById("clickme").addEventListener("click", biggerFont);
    };
    function biggerFont() {
      let button = document.getElementById("clickme");
      let size = parseInt(button.style.fontSize); // size === 16pt to start
      button.style.fontSize = (size + 4) + "pt"; // notice adding the units!
    }

    JS

    output

    Problem with reading & changing styles

    Note! Be careful to

    • remove the units from a .style value before doing arithmetic on it.
    • add the units to numerical values (like pt, px, vw, etc) when setting .style values.

    Also: a catch: you can only use this to read styles that have been set with the DOM .style earlier in the code or with inline CSS which we don't want you to do .

    You cannot read style properties set in the .css file using .style.

    Accessing elements' computed styles

    getComputedStyle method of global window object accesses existing styles

    window.getComputedStyle(element).propertyName;

    JS (template)

    #river {
      height: 600px;
    }

    CSS (in bubbles.css)

    > let river = document.querySelector("river");
    > river.style.height;
    > ""
    > river.style.height = "25%";
    > "25%"
    > window.getComputedStyle(river).height;
    > "828.234px"

    JS Console Output

    Thanks to Daniel H for the example

    Common bug: incorrect usage of existing styles

    The following example attempts to add 100px to the top of main, but fails.

    Consider the case when main has top set to "200px". Then this code would update style.top to be the invalid value of "200px100px"

    let main = document.getElementById("main");
    main.style.top = window.getComputedStyle(main).top + 100 + "px";

    JS

    A corrected version:

    main.style.top = parseInt(window.getComputedStyle(main).top) + 100 + "px";

    JS

    More on (programatic) DOM tree traversal and TextNodes

    Review: Creating New Nodes

    Name Description
    document.createElement("tag") creates and returns a new empty DOM node representing an element of that type
    document.createTextNode("text") creates and returns a text node containing given text
    // create a new <h2> node
    let newHeading = document.createElement("h2");
    // Add text to the node method 1
    let newText = document.createTextNode("This is a new heading!");
    newHeading.appendChild(newText);
    // Add text to the node method 2
    newHeading.innerText = "This is a newer heading!";
    document.querySelector("body").appendChild(newHeading);

    JS

    Traversing the DOM Tree Manually

    Every node's DOM object has the following (read-only) properties:

    Name(s) Description
    firstChild, lastChild start/end of this node's list of children, may include text or comments
    firstElementChild, lastElementChild start/end of this node's list of children elements
    childNodes array of all of this node's children
    nextSibling, previousSibling neighboring nodes with the same parent, including whitespace nodes
    nextElementSibling, previousElementSibling neighboring element nodes with the same parent, skipping whitespace nodes
    parentNode the element that contains this node

    Complete list of DOM node properties

    Browser incompatibility information (IE6 sucks)

    DOM Tree Traversal Example

    <p id="foo">
    This is a paragraph of text with a
    <a href="page.html">link</a>.
    </p>

    HTML

    Element vs. Text Nodes

    <div>
      <p id="foo">
        This is a paragraph of text with a
        <a href="page.html">link</a>.
      </p>
    </div>

    HTML

    Q: How many children does the div have? A: 3

    • an element node representing the <p>
    • two text nodes representing "\n\t" (before/after the paragraph)

    Q: How many children does the paragraph have? A: 3 (text, a, text)

    Q: The a tag? A: 1 (text)

    Common bug

    Not accounting for Text Nodes in the tree traversal

    Incorrect usage of firstChild/lastChild/nextSibling/previousSibling instead of firstElementChild/lastElementChild/firstElementSibling/lastElementSibling

    Summary of Java vs. JS vs. Python

    Final thoughts about JavaScript as a language

    Java JS Python
    Compiled vs. Interpreted Compiled Interpreted Interpreted
    Typing Strong Loose Loose
    Variable Declaration Must be declared before use Does not need to be declared before use Does not need to be declared before use
    Key Construct Classes (OOP) Function Function

    CSE 154 Modules

    1. Webpage structure and appearance with HTML5 and CSS.
    2. Client-side interactivity with JS DOM and events.
    3. Using web services (API's) as a client with JS.
    4. Writing JSON-based web services with PHP.
    5. Storing and retreiving information in a database with MySQL and server-side programs.

    MAMP

    In preparation for working with our server side code we will be installing and configuring a server on our machine called MAMP (Mac Apache MySQL PHP)

    Now instead of your urls being file:///Users/lauren/Desktop/cse154/hw2-set-bricker/set.html

    We will run them through locahost, e.g.

    • Mac:
      http://localhost:8888/154-19sp/hw2-set-bricker/set.html
    • Windows:
      http://localhost/154-19sp/hw2-set-bricker/set.html