Reminders/Admin
RegEx
Cookies
LocalStorage
CP3 due tomorrow night (or can be turned in late)
Quiz 3 coming Wendesday this week (covers async/await through today's topics)
A guide on how to install and run Node, the tool we use to write our own APIs and backends, is on the Resources page on the site.
Try to have Node ready by Wednesday's lecture
We've already seen some ways to use HTML5 tags to require certain types of input by
adding attributes to your <input>
tags to help with validation
<input type="number">
We can limit the up and down arrows with min
(and max
if we choose)
<input type="number" min=0>
To insist that there is a value in the input field we can add required
<input type="number" required>
To prevent a user from being able to type in erroneous values, we can add a
regular expression to the pattern
attribute
<input type="number" pattern="\d+">
<form id="input-form">
<div>
<label for="name-input">Name: </label>
<input id="name-input" name="student-name" type="text" pattern="[A-Z][a-z]+" required/>
</div>
<div>
<label for="email-input">E-mail (@uw.edu): </label>
<input id="email-input" name="email" type="email" required/>
</div>
<div>
<label for="sid-input">Student Number: </label>
<input id="sid-input" name="sid" type="number" min=1000000 max=1999999 />
</div>
<div id="minute-options">
<label>2-Minute Question <input type="radio" name="minutes" value=2 /></label>
<label>10-Minute Question <input type="radio" name="minutes" value=10 /></label>
</div>
<textarea name="question" minlength=20 rows=5 placeholder="Enter your question..."></textarea>
<button id="submit-btn" type="submit">Enter Queue!</button>
</form>
/**
* Just before send form data to the WPL web service.
*/
function checkInputs() {
let email = qs("input[name='email']").value;
let question = qs("textarea").value;
if (!email.includes("uw.edu")) {
handleError("Please provide a UW email");
} else if (!question.includes(" ")) // not a real question...
// Build the 5 parameters for our POST request
handleError("Please provide a descriptive question");
} else {
// could add other checks before this
submitWPLForm();
}
}
While this is a bit more work, we don't always need to send all form elements in an API request, and it also gives us more control over validation checks in JS (for properties you can't check with HTML5 attributes) before the fetch.
There are some limitations of input validation given what we've learned so far.
/^[a-zA-Z_\-]+@(([a-zA-Z_\-])+\.)+[a-zA-Z]{2,4}$/
Regular expression ("regex"): a description of a pattern of text
Regular expressions are extremely power but tough to read (the above regular expression matches email addresses)
Regular expressions occur in many places:
split
method (CSE 143
random grammar generator)
/abc/
In JS, regexes are strings that begin and end with /
The simplest regexes simply match a particular substring
The above regular expression matches any string containing "abc"
A . matches any character except a \n line break
/.ow.l./
matches "Mowgli", "Powell", etc.A trailing i at the end of a regex (after the closing /) signifies a case-insensitive match
/cal/i
matches "Pascal", "California", "GCal", etc.* means 0 or more occurrences
/abc*/
matches "ab", "abc", "abcc", "abccc", .../a(bc)*/
matches "a", "abc", "abcbc", "abcbcbc", .../a.*a/
matches "aa", "aba", "a8qa", "a!?xyz__9a", ...+ means 1 or more occurrences
/Hi!+ there/
matches "Hi! there", "Hi!!! there!", .../a(bc)+/
matches "abc", "abcbc", "abcbcbc", ...? means 0 or 1 occurrences
/a(bc)?/
matches only "a" or "abc"{x,y} matches between x and y occurrences
/a{2,3}/
matches "aa" or "aaa"Sets match on any single character used in that set
/[abc]/
matches "a", "b", or "c"/[cbl]arb/
matches "carb", "barb", or "larb"Use the ^ to match what isn't in the set
/[^a]/
matches anything that isn't the letter "a" (be careful with this one!)
Most special characters are treated normally inside of sets.
/[a.*]/
matches "a", ".", or "*"
Inside a character set, specify a range of characters with -
/[a-z]/
matches any lowercase letter/[a-zA-Z0-9]/
matches any lowercase or uppercase letter or digitInside a character set, - must be escaped to be matched
/[+\-]?[0-9]+/
matches an optional + or -,
followed by at least one digit
Practice: Write a regex for Student ID numbers that are exactly 7 digits and start with 1 (try it!)
<form id="input-form">
... rest of form
<input id="name-input" name="student-name" type="text" pattern="[A-Z][a-z]+ [A-Z][a-z]+"
title="Required name* format: 'First Last'" />
... rest of form
</form>
HTML5 adds a new pattern attribute to input elements
When an input is in a form along with a button, clicking the button
automatically verifies the input and does a POST request (can use title
parameter for more useful feedback, or the JS Constraint Validation API).
Regex can be a very handy tool with JS as well, from validation to fun find/replace features. There are two common ways regex can be used:
let pattern1 = new RegExp(/cse154/, "i");
let pattern2 = new RegExp("cse154", "i");
let pattern3 = /cse154/;
Note that we don't use "/" when using strings for patterns in the second RegExp constructor. This can be useful when we want to search for a particular pattern given as text input (e.g. a word replacer tool)!
Regex Objects have a few useful functions that take strings as arguments
regex.test(string)
returns a boolean if a string matches the regex
let namePattern = /[A-Z][a-z]+ [A-Z][a-z]+/;
namePattern.test("Robert H Thompson"); // false
let studentIdPattern = new RegExp("1\d{6}");
studentIdPattern.test("-123"); // false
Some JavaScript string methods can take Regular Expressions,
like match
, search
, and replace
string.match(regex)
returns an array of information about a match, including the index of the first match
"Hello world".match(/wo.l/); // [0: "worl", index: 6, input: "Hello world"]
origStr.replace(regex, replStr)
returns a new string replacing a pattern match in origStr with the string replStr
let newStr = "My cats are good cats".replace(/cat/, "kitten");
// newStr === "My kittens are good cats"
Question: Where might you need to be careful with a RegEx replace?
let newStr = "There is a category of cats that catch cattle".replace(/cat/, "kitten");
newStr === "There is a kittenegory of cats that catch cattle"
And what if we replaced all instances?
let newStr = "There is a category of cats that catch cattle".replace(/cat/g, "kitten");
newStr === There is a kittenegory of kittens that kittench kittentle"
Regular Expressions are a way to test or modify strings of text against a pattern.
HTTP is a stateless protocol; it simply allows a browser to request a single resource from a web server.
Once the resource has been sent to the client, the server does not keep track of any information about what was sent (other than maybe in a log file of the transaction).
But then how can websites like Amazon.com, Google, etc. remember whether you're logged in and your shopping preferences?
How does a client uniquely identify itself to a server, and how does the server provide specific content to each client?
A small (max 4kb) amount of information stored within the computer browser
Introduced in 1994 for the Netscape browser to improve shopping experience
Have many uses:
Demo example of language preference cookie (try changing the language and finding the google translate cookie in your Chrome Applications tab - what happens when you refresh the page?).
This site uses cookies in order to provide the best possible experience. Do accept our terms? Yes No
Cookies are associated with certain websites. They consist of a key and a value. They also have an expiration date, and will go away when it is reached. If an cookie does not have an expiration date, it is usually a "session cookie", cleared when the browser is closed. (the KingCounty's language cookie is a session cookie).
Cookies let us store information on a user's computer, for things like keeping them logged in even if they close the page. These are sent by the same client to the server in future requests (until they expire).
As a web developer, this is a great feature to have to improve user experience (but you should be clear about your Privacy Policy).
A great 10-minute video here with a simple overview of how different types of cookies work
Cookies are essentially just key -> value pairs with some extra information.
You can view your cookies for different sites on the Chrome Tools Application Tab
Try it out on different websites!
Client-Side
document.cookie
Server-Side (Node/Express, but also other server-side languages):
document.cookie
Returns a long string, looking something like:
"_ga=GA1.2.1860036673.1569205383; dwf_sg_task_completion=False; _gid=GA1.2.1782728754.1588304902; lux_uid=158861962030447448"
This string follows a pattern (RegEx, anyone?), like key=value; key2=value2
Can be tricky to parse correctly. So... there's another way.
Cookies are shared by clients and servers, and are fairly small (4kb)
There are Storage technologies built-in to the browser to store data that doesn't need to be stored on the server
What information can you think of that is useful to store only on the client?
localStorage
and sessionStorage
can be used to store data on the browser.
localStorage
is a window
property that allows
you to save information across browser sessions (i.e after you close the browser)
sessionStorage
is a window
property that allows
you to save information for this session only, and will be cleared when
the page is closed.
Name/value pairs (seen in cookies and Storage) are supported by most every browser
There are four useful methods for Storage
method | description |
---|---|
setItem(keyName, keyValue) | Sets the keyName location in storage to be keyValue |
getItem(keyName) | Retrieves the keyValue in storage associated with keyName |
removeItem(keyName) | Removes the keyName location in storage |
clear() | Removes all key/values in the storage |
storage-demo.html compares the two storage technologies using the code on the following slides. Inspect the Chrome Application Tab to see the difference between different browser windows.
window.localStorage.setItem("color-mode", "dark");
window.localStorage.setItem("language", "en");
window.localStorage.setItem("favorite-drink", "coffee");
let colorMode = window.localStorage.getItem("color-mode");
window.localStorage.removeItem("color-mode");
Similarly for sessionStorage
...
window.sessionStorage.setItem("color-mode", "dark");
window.sessionStorage.setItem("language", "en");
window.sessionStorage.setItem("favorite-drink", "coffee");
let colorMode = window.sessionStorage.getItem("color-mode");
window.sessionStorage.removeItem("color-mode");
But what if you want a key to hold a collection of items (e.g. a dictionary of preferences, or a current cart)?
let notes = [{ tag : "Personal", note : "Feed Orange." },
{ tag : "Personal", note : "Exercise Juice."}];
window.localStorage.setItem("notes", notes);
let data = window.localStorage.getItem("notes");
// { notes : [Object object] }
Solution: Save stringified object (possibly an array) and access as parsed JSON
window.localStorage.setItem("notes", JSON.stringify(notes));
let data = JSON.parse(window.localStorage.getItem("notes"));
// { notes : [{ tag : "Personal", note : "Feed Orange." },
// { tag : "Personal", note : "Exercise Juice." }]}
Cookies | Local Storage | Session Storage | |
---|---|---|---|
Size | 4kb | ~10mb | ~5-10m |
Expires | Manually Set | Never | When browser is closed |
Storage Location | Browser and Server | Browser Only | Browser Only |
Sent with HTTP Requests | Yes | No | No |
Note that storage limits for local/session storage depend on browser and device (Desktop vs. Mobile).
Especially if you're watching a recording, write your question on PollEverywhere and I'll answer them at the start of next lecture.
Either on your phone or computer, go to PollEv.com/robcse