Lab 5: jQuery

ENSE 374 - Software Engineering Management - Laboratory

University of Regina - Engineering and Applied Science - Software Systems Engineering

Lab Instructor: Adam Tilson


This lab introduces jQuery, a JavaScript library which makes writing JavaScript code, particularly involving DOM operations, much faster. In particular it handles things like DOM traversal and manipulation, event handling and animations. Additionally, jQuery acts as an introduction to JavaScript libraries, of which many exist, so that you can understand the process of finding, integrating and using a JavaScript library.

Computer running Windows, MacOS or Linux, with an Intel or AMD-based processor (x86 or x86-64) with administrator privileges.

  • A modern web browser, with a strong preference for Firefox or Chrome
  • A text editor, preferably VS Code

Why use jQuery

jQuery can simplify JavaScript DOM operations using shortcuts:

  • finding elements with the query selector
  • adding event listeners
  • changing css

  • Makes common things you do faster / easier
    • Because we don’t like typing!

Is it Worth Learning Today?

  • Debatable.
  • For small projects, it may be better to exclude it for performance
  • For bigger projects, it may be better to use a FrameWork like Vue, React or Angular
  • Recently BootStrap dropped it as a dependency in favour of vanilla JavaScript
  • Most of the advantages of using it are now native in JavaScript, or CSS (animations)
  • It is still useful to learn to support legacy projects using it, like ~94.4% of the web (2023).

How do we get it

You can get JQuery from the CDN!

  • Google has a good one that will likely be cached in many people’s browsers:
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> 
    

jQuery, like other JavaScript, should be added at the bottom of the HTML

  • Add it before the closing </body> tag, before your custom JavaScript.
  • Your browser reads HTML top to bottom, so we want to know about jQuery functionality before running our own scripts

in index.html

<!-- your code up here -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script src="script.js"></script>
</body>
</html>

Where can I get help?

The official docs are not bad: jQuery API

This cheat sheet is quite useful: Cheat Sheet

Main Entry

  • Create a JavaScript file, link it in the HTML (After the jQuery link), and add the following:

in script.js

$(document).ready( () => { 
 //Put your javascript in here
});
  • Recall this is callback syntax with an anonymous function being called when the document is ready. You can think of this like the ‘main’ function in other languages.

“Minify”ed Libraries

  • If you try to read jQuery code, it’s gonna be illegible. Why?
    • Minifying.
    • Removes all whitespace (which is for humans, not computers) so that we pass less data over the network.
    • The same thing happens with Bootstrap (min).
    • If you want to read the code, you need to find a non-min version of the library.

Selection tutorial

Selection is the core operation of jQuery

  • Selection rules are the same as querySelectorAll
    • Recall this is the same as CSS selectors
  • the basic selection function is: $( "..." )

e.g.

let myId = $( "#my-id" );
let myClass = $( ".my-class" );
let firstNameInput = $( "input[name='first-name']" );

What’s with the $ (dollar sign)?

In JavaScript, it’s legal to use $ as a variable name. It is used as an alias for jQuery.

  • You could, instead, use jQuery everywhere for more readable code, though that defeats the purpose of jQuery!
  • Theoretically (but unlikely) there could be conflicts with other frameworks using this variable.

Cascading

Recall from last week we can cascade function calls. This is the intended usage of jQuery, rather than storing things in variables where possible:

$( "#my-div" ).text( "Some new text" );

From vanilla DOM to JQuery

If for some reason you end up with a vanilla DOM object, and still wish to use jQuery functions on it, you will need to wrap it in the jQuery selector function:

var myHeader = document.getElementById("my-header");// found an object the old way
$( myHeader ).text( "New header text" );

From jQuery back to vanilla DOM

Conversely, if you ever wish to go back from a jQuery object to a vanilla DOM object, you can do this by using the following syntax:

$( "header" )[0]

Adding elements

To add an element, first you need to find an element relative to where you wish to insert. Then you can use the following four function to insert:

before, after -> before or after the selected element, outside the tag prepend, append -> first or last thing inside the selected element

$( "h1" ).before( "<button>new</button>" )
      <h1>     Hello World     </h1>  
   ^        ^               ^         ^
before   prepend         append     after

Note the use of HTML code in the function argument string. This is a great place to use Template Literals!

Removing elements

$( "element" ).remove();

.css("property", "value")

  • uses strings so it avoids all the awful kebab-case to camelCase nonsense we saw last week
  • If a single argument is used, it is acts as a getter.
  • If two arguments are used, it acts as a setter.

Finding a variable and setting css:

let p = $( "p" );
p.css( "background-color", "#986D8E" );

More common, we use cascading rather than saving things in a temp variable:

$( "p" ).css( "background-color", "#986D8E" );

CSS Separation of Concern

Recall last week’s tutorial

  • it’s a bad move to do CSS styles through JS / DOM,
  • instead it makes more sense just to attach or remove classes with pre-defined styles
  • using jQuery…
$( "p" ).hasClass( "big-title" )
$( "p" ).addClass( "big-title" )
$( "p" ).removeClass( "big-title" )

Setting text

Rather than DOM innerText, it’s just text

$( "h1" ).text( "Bye" );

Similarly, rather than InnerHTML, in jQuery it’s html

Note the use of setter-style, akin to Object Oriented Programming, rather than assignment style.

Manipulating Attributes

getter:

$( "a" ).attr( "href" )

setter:

$( "a" ).attr( "href", "https://www.google.com/" )

Getting Form Values

let textValue = $( "#my-text-input" ).val()

Arbitrary Data

Sometimes it’s useful to put arbitrary data in an HTML object. This can be done as follows:

<button id="btn-stars" data-star-rating="5">*****</button>
let stars = $( "#btn-stars" ).data( "star-rating" )
console.log(stars)

The syntax is a bit tricky. Data member attributes are always prefaced with the data- name when written in HTML, and then accessed from the data function using jQuery by providing pnly the name after data-. Note: this feature also exists in vanilla JavaScript using the dataset property of a vanilla DOM object.

Adding event listeners

$( "h1" ).on( "click", () => {
    // your onclick code goes here!
});

Adding KeyPress listeners

To an element…

$( "input" ).keypress( ( e ) => {
    // your onkeypress code goes here!
});

To a document…

$( document ).keypress( ( e ) => {
  console.log ( e.code );
});

Adding other listeners

$( "h1" ).on( "mouseover" , () => {
    $( "h1" ).css( "color", "purple") ;
});

Adding listeners for all buttons in a class

$('.dropdown-item').each(function() {
    $(this).on( "click", () => {
        console.log( $(this).text() )
    })
});
  • jQuery lets you animate over CSS properties
    • that is, change a CSS property over time
    • Your element starts at a starting value
      • And it ends at a value provided by you
      • You can optionally choose the time as well, and easing method
    • The value changes over time, which is called “tweening”
  • This only works with numeric values, with the exceptions of show, hide and toggle

Animating with jQuery

$("button").click(function(){
  $("button").animate({width: "500px"});
});
$( "element" ).hide(); // will hide an element, i.e. display: none
$( "element" ).show();
$( "element" ).toggle();
$( "element" ).fadeOut(); // like hide, but less jarring
$( "element" ).fadeIn();
$( "element" ).fadeToggle();
$( "element" ).slideUp();  // another type of hiding. Up => Hide, Down => Show, Toggle
$( "element" ).animate(  /*the CSS rule we want to tween to*/  );
  • animate only works for numeric properties - it’s hard to tween to things like colors

You can chain animations:

$( "element" ).slideUp().SlideDown().animate({ /*...*/ });
  • animations will play in order, left to right

To see how this works together, we are going to create an application which tracks students and their favourite colors.

  • We will allow adding new people
  • We will allow updating their favourite color via buttons
  • We will display the persons name background in their favourite color
  • We will separate the Model from the View

HTML and CSS Setup

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">

    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="text-center" id="container">
        <div id="insertion-point"></div>
        <form action="">
            <input type="text" id="new-person-name">
        </form>
        <button id="add-button">Add New Person</button>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="script.js"></script>
</body>
</html>
.red {
    background-color: red;
}

.blue {
    background-color: blue;
}

.green {
    background-color: green;
}

#container {
    background-color: light-blue;
    width: 500px;
    margin-top: 200px;
    margin-left: auto;
    margin-right: auto;
}

#container > * > * {
    margin: 10px;
}

#container > * > span {
    padding: 10px;
}

Setting up the Model

var personList = [];

function Person (id, name, color) {
    // todo: set our id, name and color!
}

Adding new names by building the model

$(document).ready( () => {
    $( "#add-button" ).click ( () => {
        //todo: create a new person with appropriate ID, name and default color 
        // get currentID, name and set a default color here!
        // use the array index as the id, e.g. 0,1,2... etc.
        // find the textbox, and use the jQuery .val() function to get its value
        // use the default color of "black"
        var person = new Person(id, name, color);
        personList.push(person);
        buildList();
    });
});

Updating Colors in the Model

function setColor (id, color) {
    // todo: find the person matching the ID and update the color 
}

Building our list

Complete the following function to actually build the list:

function buildList () {
    // remove all contents from the insertion point
    $( "#insertion-point" ).empty();
    // loop over list of people...
    for (let person of personList) {

        // append the persons name, in a <span>. Add the persons color to the span's class list.
        // hint, use template literals!

        // Create the RED button
        // Register a callback function so that, if it is pressed, we will update the model
        // so that the persons' favourite color is stored in the model, then rebuild the list.

        // Don't forget to add the RED button to the DOM!

        // Ditto GREEN button
        
        // Ditto BLUE button
        
        $( "#insertion-point" ).append( `<br>` );
        
    }
    
}

In this lab we return to the lab project. We are going to make a front-end only protoype of our application using JavaScript and jQuery, using the Model-View-Controller pattern. Recall, the end goal is to create a multi-user mesage posting application with voting capabilities. For now, we are going to simulate the applications usage using JavaScript, jQuery and DOM manipulations. We will create three sample users, selectable from a dropdown, to use the system from each of these views. Later, you will replace much of this functionality with server-side Node.js code. This lab will be primarily JavaScript / jQuery, but you can modify as needed your HTML, CSS, Bootstrap, and JavaScript.

Non-persistent interactivity

You do not need to modify the login/register page.

You do need to modify the note-vote page to include a dropdown of three sample users, as in the following screenshot:

I am using the bootstrap dropdown found here, with minimal changes:

Bootstrap 5 Dropdown Buttons

Using JavaScript / jQuery DOM manipulation, add the following functionality to the note-vote page:

  • When the Tester uses the user selection dropdown, switch the view so that it shows up as though that user was logged in.

  • When the user types in a note and adds it, it should appear in the note list.

  • Our note-vote system has the following rules

    • If a given user wrote a note, they cannot vote on it, but they can always see the score
    • If a given user did not write the note, and have not yet voted, they also cannot see the score
      • However, if they upvote the note, the upvote button becomes green and they can see the score
      • If they downvote the note, the downvote button becomes red and they can see the score
    • The score of a note is the total number of upvotes, minus the total number of downvotes

There are two extra cases to be aware of: - If a user has already upvoted, and clicks the upvote button again, it will remove the upvote - Ditto for downvote - If a user has already upvoted, and then clicks the downvote button, it will remove the upvote and assign the downvote - And vice versa

Consider the following scenario to completely understand the usage of the system.

User A logs into a brand new system and posts their first note:

Note that User A can see the score, but cannot vote.

Now we switch to user B who can vote:

Let’s have user B upvote the note:

We can see the button is now green, indicating an upvote, with a score of 1.

Let’s go back to User A:

User A can still not vote, but can see the new score:

Let’s switch to User C next:

User C has not voted yet, so they cannot see the score. Let’s have them downvote:

The score is now 0. Why? Because the one upvote and one downvote cancel each other out. Hopping back to A:

User A has no way to tell if their note is controversial, or just unseen.

Let’s hop back to B next:

User B sees the score is Zero, even though they upvoted.

By clicking on upvote again, they remove their vote:

Again, the score is hidden, as they seemingly have not yet voted.

Let’s have User B upvote again to get their score back:

We are back to the previous state:

Finally, let’s have B change their mind altogether, and move directly to a downvote:

By clicking the downvote button, their upvote is removed and a downvote is issued, making the final score -2:

Implementation:

IMPORTANT: This app must be developed as a Model-View-Controller. That means you need to separate the Model from the View.

  • You already did this last week, using JSON
    • This makes excellent test data as you are building your controller and view
  • Always perform updates according to the MVC strategy:
  • When any user interacts with the system…
    • First update the model (change something in the list of JavaScript Objects)
    • Then use the updated model to update the view
    • The easiest way to do this is remove all view elements, and recreate from the view from scratch

Submission

This is a submission based lab, please submit to URCourses by the due date.

Please include HTML, CSS and JavaScript files so that the complete system may by run by the grader.