Module codes: |
CS1115/CS5002 Web Development I CS1116/CS5018 Web Development II |
---|---|
Credit weighting: | 5 credits each |
On-campus lectures | Tuesdays 12-1pm, WGB 107 |
Fridays 1-2pm, WGB 107 | |
On-campus labs | Thursdays 2-4pm, WGB 110/ Thursdays 4-6pm, WGB 110, starting in week 2 |
Private study | At least 3 hrs per week |
Continuous assessment: | Server-side programming project, 50 marks |
---|---|
Continuous assessment: | Client-side programming project, 50 marks |
Students should be aware that the use of ChatGPT or Copilot or similar tools to generate text, code or diagrams without clear attribution will be considered an academic offence equivalent to plagiarism.
The slides, screenshots, labs, videos and other resources are mine
(and UCC's)!
Please do not republish them anywhere!
Many web apps use both!
To learn server-side programming, we will use Flask.
Flask is a microframework for writing web apps in Python.
Compile-time | Syntax errors | Error message in the terminal |
---|---|---|
Run-time | Asking Python to execute something that is impossible | Error message in the browser (depending on .flaskenv ) |
Logic | The output is not what we want | No error message! |
The form
element contains 'controls' such as textfields, radio buttons, etc.
Most controls are specified using the input
tag.
The type
attribute specifies what kind of control:
Click to see all of them!
A few controls use a different tag,
e.g. textarea
, select
<input type="text" />
… allows many additional attributes, e.g.:
name
size
maxlength
value
placeholder
name
attributeMost controls are useless unless given a name.
Why?
Because we'll be using their names in our Python.
size
attribute
By default, textfields are 20 chars wide,
but optionally use the size
attribute
to change its width:
<input type="text" />
<input type="text" size="30" />
maxlength
attribute
By default, users can type as many chars as they want (irrespective of size
),
so optionally use maxlength
to restrict this:
<input type="text" />
<input type="text" maxlength="20" />
value
attribute
By default, textfields are initially empty,
but optionally use the value
to supply your own initial value:
<input type="text" />
<input type="text" value="2" />
placeholder
attributeHow do users know what they are supposed to type?
label
:
<label for="age">Age:</label>
<input type="text" id="age" />
placeholder
:
<input type="text"
placeholder="Type your age" />
placeholder
is good when you are short of space.
When the user presses the submit button, the browser arranges the data in name/value pairs.
given_name=Hugh
family_name=Jeegoh
The browser also encodes the data using standard URL encoding.
This replaces spaces, slashes and other characters that are not allowed in URLs by their hexadecimal equivalents.
given_name=Ann
family_name=O%20Domini
method
attribute
action
attribute
If the method="get"
,
the data is joined by ampersands
and added to the end of the URL after a
question mark:
If the method="post"
,
the data is in the HTTP request body:
GET | POST |
---|---|
for a short form with few fields | for a longer, more complex form, where encoding the form data in the URL would result in a URL that is too long |
to allow the URL (with the form data) to be bookmarked, or used elsewhere | for security, because encryption and other encodings of the data is possible when it is the HTTP body |
where the outcome wouldn't differ if you issued the same request more than once | for requests that make a change in the server (e.g. update a database) |
Flask programmers pretty much always use POST.
This is given by the form's action
attribute.
If we write action=""
, the data is sent back to same URL that the form came from.
try:
weight = float(weight)
height = float(height)
...
except ValueError:
return render_template(..., error="Error!")
try
blockexcept
blocktry
blockexcept
blockid | name |
---|---|
123 | Hugh Jeegoh |
234 | Julie Noted |
code | title |
---|---|
CS345 | Python for Poets |
CS456 | Databases through Dance |
id | code |
---|---|
123 | CS456 |
234 | CS345 |
234 | CS456 |
Find the id's and names of all students who study CS345
SELECT Students.id, Students.name
FROM Students JOIN Enrolments
ON Students.id = Enrolments.id
WHERE Enrolments.code = "CS345";
Students.id | Students.name |
---|---|
234 | Julie Noted |
DROP TABLE IF EXISTS gigs;
CREATE TABLE gigs
(
gig_id INTEGER PRIMARY KEY AUTOINCREMENT,
band TEXT NOT NULL,
gig_date TEXT NOT NULL
);
INSERT INTO gigs (band, gig_date)
VALUES ('Decaying Shroom', '2025-01-12'),
('Belated Tonic', '2025-01-21'),
('Dumpy Tension of the Divided Unicorn', '2025-02-10'),
('Belated Tonic', '2025-02-20'),
('Missing Roller and the Earl', '2025-02-26'),
('Glam Blizzard', '2025-03-07'),
('Piscatory Classroom', '2025-03-12'),
('Prickly Muse', '2025-03-20'),
('Interactive Children of the Phony Filth', '2025-03-29');
Band names produced by www.bandnamemaker.com
HTTP is a stateless protocol:
each request is independent
— by default, the server
has no memory of previous requests
Adequate for HTTP's original purpose:
Inadequate for situations where it can be useful to recognise repeat contacts, e.g.:
IP addresses | keep a record of clients' IP addresses |
---|---|
Cookies | send the client some data, which it sends back to you |
URL rewriting | include a user identifier in the query part of a URL |
Hidden fields | include a non-visible field in forms |
A cookie is a small amount of data (a name/value pair)
id=cust123
www.amazon.co.uk
:
GET /index.html
HTTP/3.0 200 OK
Set-Cookie: id=cust123; path=/; domain=.amazon.co.uk
…
www.amazon.co.uk
again
(or the amazon.co.uk
domain)
GET /index.html
Cookie: id=cust123
HTTP/3.0 200 OK
Set-Cookie: id=cust123;
expires=Sun, 17-Jan-2044 19:14:07 GMT;
path=/; domain=.amazon.co.uk
…
HTTP/3.0 200 OK
Set-Cookie: id=cust123; path=/; domain=.amazon.co.uk
…
DROP TABLE IF EXISTS wines;
CREATE TABLE wines
(
wine_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL,
description TEXT
);
INSERT INTO wines (name, price, description)
VALUES
('Dingo Dribble', 12.33, 'Goes well with a snake steak.'),
('Emu Emission', 17.99, 'A spunky wine for every occasion!'),
('Koala Kool-aid', 12.33, 'With a hint of eucalyptus.'),
('Platypus Pish', 15.99, 'A fizzy and frothy concoction.'),
('Roo Runoff', 8.99, 'Quite acidic.'),
('Wombat Waz', 10.99, 'The taste of the outback.');
Consider the Hello World program that we wrote in the lecture:
<script>
start tag and </script>
end tag?
window.alert()
method. Why?
Array
, Date
and Math
JavaScript variables should be explicitly declared
(using let
or const
)
hourly_pay = 9.5
hours_worked = 35
total_pay = hourly_pay * hours_worked
print(total_pay)
# Hurray! A pay rise:
hourly_pay = 10.5
total_pay = hourly_pay * hours_worked
print(total_pay)
let hourly_pay;
let hours_worked;
let total_pay;
hourly_pay = 9.5;
hours_worked = 35;
total_pay = hourly_pay * hours_worked;
console.log(total_pay);
// Hurray! A pay rise:
hourly_pay = 10.5;
total_pay = hourly_pay * hours_worked;
console.log(total_pay);
But JavaScript does allow you to combine variable declaration with initialization
hourly_pay = 9.5
hours_worked = 35
total_pay = hourly_pay * hours_worked
print(total_pay)
# Hurray! A pay rise:
hourly_pay = 10.5
total_pay = hourly_pay * hours_worked
print(total_pay)
let hourly_pay = 9.5;
let hours_worked = 35;
let total_pay = hourly_pay * hours_worked;
console.log(total_pay);
// Hurray! A pay rise:
hourly_pay = 10.5;
total_pay = hourly_pay * hours_worked;
console.log(total_pay);
Single-line comments
# This is a comment
x = 3 # This is also a comment
// This is a comment
let x = 3; // This is also a comment
Multiline comments
"""
This is another comment.
It extends over more than one line.
"""
/*
This is another comment.
It extends over more than one line.
*/
def print_1_to_n(n):
for i in range(1, n+1):
print i
print_1_to_n(10)
function print_1_to_n(n) {
for (let i = 1; i <= n; i += 1) {
console.log(i);
}
}
print_1_to_n(10);
NB: Python uses indentation to denote blocks of code;
JavaScript uses curly braces
context.fillRect(x, y, width, height)
x |
The x-coordinate of the upper-left corner of the rectangle |
y |
The y-coordinate of the upper-left corner of the rectangle |
width |
The width of the rectangle, in pixels |
height |
The height of the rectangle, in pixels |
context.clearRect(x, y, width, height)
x |
The x-coordinate of the upper-left corner of the rectangle to clear |
y |
The y-coordinate of the upper-left corner of the rectangle to clear |
width |
The width of the rectangle to clear, in pixels |
height |
The height of the rectangle to clear, in pixels |
window.requestAnimationFrame(function)
function |
The function that will be executed |
if x < y:
print("x is smaller than y")
elif x == y:
print("x is equal to y")
else:
print("x is larger than y")
if (x < y) {
console.log("x is smaller than y")
} else if (x === y) {
console.log("x is equal to y")
} else {
console.log("x is larger than y")
}
In the JavaScript, note:
else if
instead of elif
Also, where Python uses and
, or
and not
, JavaScript uses &&
,
||
and !
Python is a strongly-typed language,
whereas JavaScript is weakly-typed
x = "abc" + 12
x = "abc" - 12
if "abc" == 3:
# do something
else:
# do something else
if "3" == 3:
# do something
else:
# do something else
x = "abc" + 12
x = "abc" - 12;
if ("abc" == 3) {
// do something
} else {
// do something else
}
if ("3" == 3) {
// do something
} else {
// do something else
}
===
), instead of equality (==
)
let twinA = {
firstname : "John",
surname : "Grimes",
age : 32
}
let twinB = {
firstname : "Edward",
surname : "Grimes",
age : 32
}
twinA.firstname
The 'equivalent' of a Python list is a JavaScript array
groceries = ["eggs", "milk", "tea"]
len(groceries)
groceries.append("bread")
let groceries = ["eggs", "milk", "tea"];
groceries.length
groceries.push("bread");
(JavaScript arrays are very similar to Python lists
but not so similar to arrays in languages
such as C or Java)
for
loopsUsing a loop to 'visit' each item in a list or array
for item in groceries:
print(item)
for (let item of groceries) {
console.log(item);
}
Using a loop to count, e.g. from 0 to 9 inclusive
for i in range(10):
print(i)
for (let i = 0; i < 10; i += 1) {
console.log(i);
}
function annoy_me() {
window.alert("Annoying, huh?");
}
annoy_me();
window.addEventListener("click", annoy_me, false);
clientX
and clientY
— the viewport coordinates of
the mouse click
function annoy_me(event) {
window.alert("Your click was at " + event.clientX + " " + event.clientY );
}
window.addEventListener("click", annoy_me, false);
In fact, our JavaScript programs already contained an example of event-driven programming
Q: Where?
document.addEventListener("DOMContentLoaded", init, false);
let playerImage = new Image();
init
), e.g.:
playerImage.src = "image.png";
draw
):
context.drawImage(playerImage, 10, 20)
— here we place the top left of the image on the canvas at 10 pixels across
and 20 pixels down
context.drawImage(image,
sx, sy, swidth, sheight,
dx, dy, dwidth, dheight)
image |
The image we are extracting from |
sx , sy , swidth , sheight |
The x- and y-coordinates of the upper-left corner of the part of the image that we are drawing, and the width and height |
dx , dy , dwidth , dheight |
The x- and y-coordinates of where in the canvas we want to draw the upper-left corner of the part of the image that we are drawing, and the width and height |
window
object
navigator
objectlocation
objectscreen
object andhistory
object
<!DOCTYPE html>
<html>
<head>
<title>A simple document</title>
</head>
<body>
<p>
Some words.
</p>
<p>
More words
<em>and emphasised words</em>
and final words.
</p>
</body>
</html>
Note the different types of node:
the document node, the element nodes, and the
text nodes
element = document.querySelector("...");
null
if no element matches the CSS selector
elements = document.querySelectorAll("...");
<html lang="en">
<head>
<title>Lorem ipsum</title>
</head>
<body>
<section id="sectA">
<p class="opening">Lorem ipsum dolor sit amet consectetuer adipiscing.</p>
<p class="closing">Lorem ipsum dolor sit amet consectetuer adipiscing.</p>
</section>
<section id="sectB">
<p class="opening">Lorem ipsum dolor sit amet consectetuer adipiscing.</p>
<p>Lorem ipsum dolor sit amet consectetuer adipiscing.</p>
<p class="closing">Lorem ipsum dolor sit amet consectetuer adipiscing.</p>
</section>
</body>
</html>
element = document.querySelector(".opening");
elements = document.querySelectorAll(".opening");
elements = document.querySelectorAll("#sectB .opening");
NB: It's still an 'array'.
p
element node:
new_element = document.createElement("p");
some_element.appendChild(new_element);
— it becomes the last child.
insertBefore
, replaceChild
, removeChild
new_element.innerHTML = "Third paragraph";
innerHTML
is even more powerful than this example suggests
some_element.id = "new_id";
id
, src
and other attributesclass
attribute:
some_node.class = "new_class";
some_node.className = "new_class";
style
some_element.style.color = "blue";
CSS | JavaScript |
---|---|
background-color
|
style.backgroundColor
|
font-family
|
style.fontFamily
|
A browser sends an HTTP request to a server…
But a client-side script can also send a HTTP request
XMLHttpRequest
objectsSummary of what your client-side script needs to do in order to send an HTTP request:
XMLHttpRequest
example
// Create the request object
let xhttp;
xhttp = new XMLHttpRequest();
// Register a function that will handle the response:
xhttp.addEventListener("readystatechange", handle_response, false);
// Specify the URL and the HTTP method (e.g. GET or POST):
xhttp.open("GET", url, true);
// Optionally, specify any special headers that are to be sent, e.g.:
xhttp.setRequestHeader("User-Agent", "XMLHttpRequest");
// Send the request:
xhttp.send(null);
xhttp
)
xhttp.status
: the status code sent back
by the serverxhttp.getResponseHeader("…")
: to
access the specified response header
xhttp.getAllResponseHeaders()
: to
access all response headers as an array
xhttp.responseText
: to access the
body of the server's response as a string
xhttp.responseXML
: to access the body
of the server's response as XML (inc. HTML)
function handle_response() {
// Check that the response has fully arrived
if ( xhttp.readyState === 4 ) {
// Check the request was successful
if ( xhttp.status === 200 ) {
// do whatever you want to do with
// xhttp.responseText or xhttp.responseXML
} else {
console.log("Problem " + xhttp.status);
}
}
}
Asychronous JavaScript and XML (Ajax):
XMLHttpRequest
XMLHttpRequest
to
fetch from the server just the content that has changed
Ajax is used to build single-page apps
XMLHttpRequest
s is quite convolutedfetch
function