[MUSIC PLAYING]
SPEAKER 1: Welcome back, everyone, to Web Programming
with Python and JavaScript.
And let's pick up where we last left off.
So over the course of the past couple of weeks,
we've been working on designing web applications, primarily using Flask.
And if we think about web applications in terms of clients and servers--
where the client is the user of the application on their computer who goes
to your web address and is interacting with your web application,
and your server is where your Flask application code is running--
then you can think about where code is actually
running in terms of this diagram.
And so so far, when we've been writing Python code in Flask, all of that code
has been running on the server.
So the user on their computer makes a request for a particular web page.
I have either Get, or Post, or some other HTTP method.
That request is made to our web server, which is running in Flask with Python.
That server tries to process that response,
understand what it is that the client is asking for.
And then ultimately, that server sends back some HTML and maybe CSS
content back to the client, which is rendered in that client's web browser.
What we're going to transition to today is
looking at code that doesn't run on the server but rather runs
instead on the client, on the user's own computer inside of their web browser.
Now, what might be some reasons why we would want code that runs on the client
as opposed to just on the server like we have
been over the course of the past couple weeks?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Sure.
Certainly.
So one possible reason is that if you have
a lot of users that are all trying to access
your application at the same time, than anything
you can do to take some of the load off of the server
so that you don't have too many clients all trying to connect to the server
simultaneously, that can help to reduce the load on the server.
Fantastic.
And that would be one reason why we might want code running on the client.
Why else might we want code, in some cases, to run on the client
instead of on the server?
Other thoughts or ideas?
Yeah?
AUDIENCE: It's faster to [INAUDIBLE] have to go to the server and back.
You can just [INAUDIBLE].
SPEAKER 1: It's faster.
Sure.
So if we want something to happen and that something or computation doesn't
need to be something that the server is processing,
then we may as well just have the client run that code in the web browser
as opposed to the additional latency, the additional time
that it would take for the client to make a request to the server,
for the server to run the code, and then the server
to respond back to the client.
And so these are some reasons, as we'll soon see,
where it becomes more powerful and more useful to have code that's actually
running inside of the web browser.
And that's what we're looking at over the course of today.
So to do that, we're going to be introducing another language.
We spent the last couple of weeks working in Python,
but today we're going to introduce JavaScript,
which is a language really designed for usage to be run inside of web browsers,
although now it's used elsewhere as well.
But primarily, we're going to be using JavaScript inside of web browsers
to run code on the client, so that we don't
need to talk to some external server necessarily
for all of the computational work that we need to do.
Now, JavaScript has come in a number of different variations
and it's been updated over time.
And different browsers obey slightly different variations
of the language, where some web browsers support some features of JavaScript
and others support other features.
And so in order to manage all of this complexity of different versions
and different features of JavaScript, there
are certain standard versions of JavaScript
that have been released over the years, such
that we can make sure that most modern browsers will
be compliant with particular standards.
And the version of JavaScript that we're going to be using in this class
is one of the more popular, more recent versions of JavaScript known as ES6.
And so this is just one particular standard version of JavaScript
that pre-defines a certain list of features, some of which
are new features that weren't in previous versions of JavaScript.
But most web browsers nowadays support this latest version of JavaScript ES6.
And so that's going to be the version that we're primarily
going to be using in this class.
So let's take a look at some actual JavaScript code
and what it might look like if we were to run it.
And so if we were going to try to put some JavaScript code inside of a web
page, it might look something like this.
This would be a very simple JavaScript section of a web page.
And what you'll notice is that it's enclosed inside of script tags.
We saw HTML tags over the course of several weeks prior.
And what we're going to be looking at with JavaScript
is JavaScript code that is inside of our HTML pages,
located, in particular, inside of these script tags.
And in this case, what we have appears to be a calling a function.
We have a function called alert.
And inside the body of this function, we have the phrase "Hello, world!"
And so let's take a look at this code actually in action
if we were going to try to run this inside of a web page.
So I go ahead and open up hello0.html.
This is just an HTML document, a web page
where we have some web content here.
You'll notice that, just like before, we have a head section to the web page,
we have a body section to the web page, where all that's inside of it
is one big heading that says "Welcome."
And inside of these script tags, we have this call to an alert function.
And that alert function just has, as its argument, the string "hello!"
with an exclamation point.
And as you might guess, this alert function
is a function that is part of JavaScript.
And what it will do is it will display an alert window for us.
So if I were to now open hello0.html, what
I get is an alert in my web browser that says, this page says "hello,"
exclamation point.
And I would press OK.
Now I see the rest of that web page.
So that was our very first JavaScript application, or web page.
And the way we were able to make that happen
was just by including JavaScript code inside of our HTML document,
so long as it is enclosed inside of these script tags.
And so that was a very simple example of how
we might want a JavaScript code to run when it's inside of a web page.
What you probably noticed is that I didn't
have to do anything other than just open this web page in order
for this JavaScript code to run.
As soon as I opened the page, it gave me the alert that said "hello,"
and that was that.
What we likely want to do, though, as we begin to develop more sophisticated web
pages is make it so that all of the JavaScript code
isn't running right all at the beginning all at once.
But instead, we want certain JavaScript code only to run or only
to execute after certain things happen or when certain events happen,
so to speak.
And so in order to do that, what we'll do is we'll take a look at events.html.
And what you'll notice here is inside of the script tag, just like before,
rather than just immediately say, alert hello,
where "hello" is the argument that's being passed into this alert function,
we're instead defining a function.
And so this function is called hello.
It takes no arguments.
And inside the body of this function, we say, alert('Hello!').
And you notice that JavaScript will use these curly braces
to denote things that are contained within the function,
unlike Python which used indentation to denote what's inside of the function
and what's not.
These are just syntactic differences, but the general idea
is going to be the same thing, that hello here
is a function, some block of code that we can later call on when
we want the alert message to appear.
But notice inside of the script tag, we never actually have any code that says,
run the hello function.
We just have these three lines that are defining,
this is what the hello function is, but no actual use case in which
we are running that hello function yet.
But if we look down later in the HTML page,
we have this heading that says "Welcome."
And we also have this button.
And the button says, click here, just defined by button tags
with "click here" inside of those tags.
But we've given this button an additional attribute-- onclick.
And inside of its onclick attribute, we've
set it equal to "hello," the JavaScript code that we want to run.
We want to run the hello function when this button is clicked.
And the way to think about this from a JavaScript perspective
is that JavaScript understands some certain events, where
click is an event, where if you click on a button,
that triggers the click event, so to speak.
And when that click event is triggered, when that button is clicked on,
when I click on Click Here, what should JavaScript do?
Well, in this case, I'm telling the web browser
that I want to run the hello function that I've defined up here earlier
inside of these script tags.
And so the result of this is if I open events.html,
no alert message pops up right away.
I just see "Welcome!" as the heading.
And then I also see this Click Here button down below.
And it's only when I click that button that the click event is triggered,
which triggers the calling of the hello function inside of my JavaScript.
And the result of that is that when I click there,
then I get the pop-up window that says, This Page Says Hello.
And if I were to click on it multiple times, I would see it again and again.
This is equivalent to me calling that function on multiple occasions.
So questions about what we've seen so far in terms of JavaScript code,
and enclosing things in script tags, and events?
Yes.
AUDIENCE: Can you go back to the first one?
SPEAKER 1: Yup, so this was the original code that we started with.
AUDIENCE: Can you put the script after the [INAUDIBLE]??
SPEAKER 1: Good question.
So the question is, what would happen if you took these script tags
and placed it after "Welcome," just placed it down there,
and then tried to open it?
In this case, it still says This Page Says Hello before you see it.
And this, I think, is just Chrome that is processing the alert message first
before it goes on to dealing with the rest of the content of the page.
Other questions?
Yeah.
AUDIENCE: Is that for all JavaScript or just [INAUDIBLE]??
SPEAKER 1: I'll have to get back to you on that,
but generally speaking, JavaScript will try
to execute top to bottom, though there are certain cases in which it
can behave in asynchronous ways.
And we'll take a look at a couple examples of that
later on in the lecture.
OK.
So let's do a quick recap of the things we've talked about so far.
And so what we saw just now was an example of functions in JavaScript,
where a function can be defined just by the word "function" followed
by the name of the function that we want to define followed by, optionally,
some number of arguments that that function is taking.
And then enclosed in curly braces, we have all of the code
that we want to run when that function is executed.
So in this case, we want to run an alert that says "Hello World,"
or just "Hello," or something else.
And in particular, we were calling these functions
when particular events happened.
So we saw that before, this hello function only happened when
we clicked on a button, for example.
When the onclick event was triggered, that
resulted in the hello function being called.
But there are other events as well.
And so here are some other common events that you might see.
This is not a comprehensive list, but some common events that may come up
and that may be useful if you want particular blocks of JavaScript code
to only run under certain circumstances or under certain conditions.
So the onclick event was the one we just saw
that happens if there's a button, for example,
and someone clicks on that button.
onmouseover is good if you want something
to happen if someone hovers over a part of the window as opposed
to having to actually click on it.
So onmouseover works well for that.
There are also keyboard events related to not what's being done with the mouse
or with the cursor, but what's being done with the keyboard itself.
onkeydown is an event that's triggered when someone presses down on the key.
And when they release that key, that triggers the onkeyup event.
onload is one that happens when something
is finished loading, like when the entire window is finished
loading with all of its images, and CSS resources,
and everything else that's included in the window of the page.
And onblur is also a common one that used when an object loses focus.
So for example, an input field.
If you're in the input field, and it's highlighted in blue,
as you might commonly see, and then you click out of it, and it loses focus,
you can use events like onblur in order to handle situations like that.
And so by running different functions in response to different events,
the result is that we can begin to build much more dynamic user
interfaces, where it's not just that the server responds to the client,
and the client sees a web page, and they can just
view the contents of that web page, but they can actually
interact with the web page.
So they can click on buttons that trigger actions to happen
without needing to request additional information from the server,
but just because JavaScript code is going to be running inside
of the user's own web browser.
And so these events can ultimately help in order to make some of that possible.
So now what we'll take a look at is how we
might begin to use some of the information
that we are able to look at inside of the DOM,
inside of the Document Object Model, the contents of the web page,
in order to write JavaScript that is able to access and manipulate
the DOM-- in other words, using JavaScript code that actually
manipulates the contents of the web page.
So far we've just seen JavaScript used to display an alert, for example,
but it actually is far more powerful than that.
And so what I'm going to introduce is the idea
of querying for something in the documents,
searching for something inside of the document of the web page
in order to make something happen.
So let's take a look now at query.html.
And so inside of the body of this web page, right now
it just, again, has a heading that says "Welcome!" and a button that says
Click Here, where, when we click on that button,
it's going to trigger the hello function.
But what's actually happening inside of the hello function?
Let's take a look at it.
So inside of these script tags, inside of the JavaScript contents
of the web page, we've defined a function called hello, same as before.
And that hello function is triggered whenever
this button down here is clicked on.
So when someone clicks the Click Here button,
it's going to run the hello function.
Now, instead of just alerting something inside of this function,
let's see what's happening here.
Document is just a variable in JavaScript
that refers to the web document, the web page that we're currently displaying.
And querySelector is a special function that
will allow us to search through the contents of the web
page for a particular CSS selector.
So if you recall from way back when we were
talking about CSS in the first and second weeks of the course,
we had different ways of using CSS selectors in order to style
particular elements of the text.
You recall that we could say li, for instance, followed by some CSS code
to style all of the list elements.
Or we could use the pound sign in front of the name of an ID
in order to style only that particular ID.
Or the dot sign followed by the name of a class
to style all of the different things that belong to that class.
We can use that same syntax and the same sophistication that we could use in CSS
selectors inside of this querySelector function,
which will extract from the website one particular HTML element.
So in this case, I'm querying for an H1 element.
And if we were to search through this web page just using our own eyes,
we would find that the H1 element is down here.
And then we're accessing the .InnerHTML property of this H1 element.
Any ideas what the inner HTML property of the H1 element
could refer to in this case?
So the H1 tag--
it's a good guess.
So this is, in fact, the element.
But the inner HTML would refer more specifically to-- yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Precisely.
The content in between the opening and closing tags of H1.
So if I querySelect for the H1, that will select this H1 element.
And the inner HTML of that H1 element is the HTML content
that's contained within those tags.
So in this case, it's just the word "welcome."
And so now if I instead set that inner HTML to "goodbye,"
the result is that I'm able to use this JavaScript code to actually edit
the contents of the DOM, edit the contents of the web page
in order to change what's contained inside of the body of the page.
Yeah, question?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Good question.
So can you make a button called Multiple Functions?
Certainly inside of these quotation marks here,
we could add more sophisticated JavaScript
that potentially did call multiple functions.
But ultimately, this is going to start getting messy
if we have a lot of different functions contained inside
of this onclick attribute.
We'll see very shortly how we can begin to factor
some of this out of the actual HTML contents
in order to use just plain JavaScript inside of these script tags
that allow us to do much of the same thing.
But yes, you can.
Yeah?
AUDIENCE: Will it change all the H1s if there
were multiple H1s in the document?
SPEAKER 1: Great question.
What would happen if there were multiple H1 tags is the question?
Would it change all of them?
The answer is no.
What querySelector will do is it will look
through the web page for something, but it will only
select the first thing that it finds.
So if there were multiple H1 tags, it would only select upon that first one.
We'll look at least one example later where
we want to select for multiple things using querySelector.
And we'll see how we can do that as well.
But just this querySelector function will only
select one thing that matches what we're searching for.
So let's see this page in action.
So this is query.html.
So if I now open query.html, what I see is the word "Welcome!"
in my big heading and this button that says Click Here.
And recall, when I click the Click Here button,
that will trigger the calling of the hello function, which,
ironically enough in this case, changes the contents of H1
to say "goodbye" instead of "welcome."
And if I refresh the page, now it goes back to "Welcome!"
because I reloaded the entire page again.
But clicking the button again takes me back
to saying "goodbye" instead of "welcome."
And so I've been able to manipulate the contents of the web
page using JavaScript without needing to contact another server.
I didn't load another web page that said goodbye.
This is the same web page.
I've just used JavaScript in order to modify the contents
of what was actually on that page.
Questions about anything so far?
Yeah.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Is what case-sensitive?
AUDIENCE: Attribute [INAUDIBLE].
SPEAKER 1: The property, I believe, is case-sensitive, yes.
AUDIENCE: So something like a getID--
so it went [INAUDIBLE].
SPEAKER 1: Good question.
So the question is about a different function called getElement by ID.
And that is, I believe, case-sensitive.
And the way getElementById works, which you won't actually see today,
is it's very similar to querySelector except it will only select something
by its particular ID name.
And just to give you a brief overview of the simple things
that you can do with querySelector, what we saw just now
was using document.querySelector to select a tag,
like an H1 tag in this instance.
We could also have used querySelector to select for a particular ID
just by using the pound sign in front of it.
This is the equivalent of what getElementById would do
or, alternatively, by a particular class just by using the dot in front of it.
And this might be syntax you remember from way back when
we were talking about CSS.
And if you recall, we can also use the more sophisticated CSS selectors.
Remember we talked about selectors that would
allow us to access just the children of a particular type of element
or looking at only input fields that were of a certain type.
We can do all the same things using querySelector
in order to extract from the HTML page just the element that we
care about modifying in this case.
So let's start to take all of these features
and put them together into an application that
does something a little more than just replace the contents of a heading,
for instance.
And let's take a look at counter0.html.
And so counter0.html, I'll show you what it's ultimately going to do,
and then we'll look at the code that makes this happen.
Right now the button that says Click Here.
And my heading, instead of being some text, is just the number 0.
And you'll notice that when I click on Click Here, the 0 changes to 1.
If I click again, it changes to 2.
And then the more I click, the number will just
keep counting higher and higher and higher, incrementing by one every time.
So what is the code that I need in order to make something like this happen?
Well, let's take a look at counter0.html.
So first, let's look at the body of this code.
I have an H1 and I've given it an ID.
And I've called the ID counter.
This is just so that later on I can access this particular
heading by referencing it by its ID.
Its ID is counter, and this gives me an easy way to get at this heading.
And by default, it's just going to say 0 to begin with.
And this Click Here button is the same as the buttons we've seen before.
It's a Click Here button, where, when we click on it,
we'll call the count function.
What's now happening inside of the script?
Well, inside of the script, the first thing we've done is define a variable.
Just like variables exist in Python that allow us to store values and manipulate
values, we can likewise have variables existing in JavaScript as well.
And the syntax here for that is let counter equal 0.
We've defined a variable called counter, and its initial value is going to be 0.
And so that variable now exists inside of our page.
And now underneath it, we've defined a function called count.
And what the count function does is it will increment the counter.
Counter++ is just another way of saying, counter equals counter plus one.
It sets counter to be a value one larger than it.
And then after we've incremented counter,
we're doing document.querySelector #counter
in order to extract the item that has ID with name counter.
And then taking that HTML element, which is the heading down below,
and setting its inner HTML to be whatever
the value of this counter variable currently is.
And so the result of that is that if counter begins at 0, then
when I click the button for the first time
and the count function gets called, counter on line 9
here is incremented to be 2 or 1 instead of 0.
And then we select that heading and update the inner HTML of it
and update the contents of that heading to be the new number.
So questions about that code and how we were able to make that work?
All right.
So now we'll take a look at some other features that we can begin to use.
We'll take a look at counter1.html.
And in particular here, we'll start to look at conditions
that we can add to code.
So just like Python has loops, and conditions, and all
of these other programming constructs that
let us do interesting things with our code,
we can do the same thing in JavaScript.
Although the syntax is a little bit different,
the ideas, fundamentally, are all the same.
And so here we have, inside of our body, a heading
that's called counter that starts off at zero,
and a button where, when we click on it, it updates the count.
And inside of these script tags here, we are
going to define some JavaScript that's very similar to the code we've
been using before.
let counter equal 0 is the definition of our variable counter, which
starts off at zero.
And inside of this count function, after we increment the counter,
after we update the inner HTML of our counter heading to be whatever
the current value of the count is, we have this condition,
which is that if counter % 10 equals equals equals 0,
which you should read as, if counter mod 10,
where mod is the operator where you take the remainder of one thing divided
by the other-- in other words, if you take one number and divide it by 10,
whatever the remainder is, that will be what counter mod 10 is equal to.
And the triple equal sign is JavaScript's way
of comparing things for exact equality.
In other words, they have to be identical to each other in order
for this to hold true.
So if counter mod 10 is equal to 0, then we alert and say the counter is at--
and here, this is an interesting feature of the more recent versions
of JavaScript, including ES6, where this is known as a template literal.
You think of them like formatted strings in Python,
where I want to alert something, but I don't just
want to alert some string that is always going to stay the same.
I want to dynamically generate that string
with some contents of variables that exist inside of my JavaScript code.
And so to do something like this, the way you
would do that is instead of using quotes,
either single or double quotes as you normally would for strings,
we're using that back tick symbol, which, on US keyboards,
is right above the Tab key.
And we say in this alert, the counter is at--
and then to plug in a variable, the way we do this in JavaScript
is to use the dollar sign and then, inside
of curly braces, the name of the variable that we want to plug in.
And so here, we're saying, counter is at--
and this code here is saying, take the variable counter
and just plug it in to this part of the template string.
So it's the back ticks around the string, and then the dollar sign,
and the curly braces that allow me to format the string
and treat it like a template that I'm able to plug in certain variable values
into it.
So what do we think the result of running this web page is going to be?
How is the behavior going to be different than the example we
saw just a moment ago?
In particular, what will this if condition inside of the count function
do?
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Great.
Every 10 times we click the button, it should pop up
with an alert, because every 10 times we click it, when we get to counter
is 10, then 10 mod 10, 10 divided by 10, leaves a remainder of 0.
And so 0 will be equal to 0, and then we'll get the alert.
But in other cases, we won't.
So if we now open up counter1.html, we can count just as we did before.
But it's only when we are at 9.
And when we click again, then we get the alert saying that the counter is a 10.
And if we were to continue counting, when we get to 20,
it would give me another alert, 30, so on and so forth.
And so this allows us to have a little more control.
Just like we were able to have if conditions inside of Python,
we do the same thing in JavaScript.
And like the distinction between functions in Python and JavaScript,
the way that we denote the contents of an if condition in JavaScript
is to enclose it inside of curly braces.
Strictly speaking, this is only necessary
for if conditions that run onto multiple lines.
If the condition were just a single line, as it is now,
the curly braces aren't, strictly speaking, necessary.
But oftentimes, they're good just as a visual cue so that anyone
looking at the code can see what is contained inside of the if condition
and what is not.
Questions about any of that so far?
OK, great.
Now let's take a look at counter2.html.
And so in this case, what we've done is if we look down at the body
before we look at the actual contents of the JavaScript,
we'll notice that one thing is different here.
We have H1 id is counter is 0, and then a button that says Click Here.
What is different between this and all of the other buttons
that we've seen so far?
Great, the onclick property is no longer present.
So I have this button that says Click Here,
but I don't have anything that is saying, when this button is clicked,
here's what you should do.
Or at least it doesn't appear to be that way.
What we'll soon see is an attempt at trying
to take all this JavaScript logic and JavaScript code,
and factor it out of the HTML.
So I don't need to have JavaScript code inside the body of the HTML contents
in order to tell the button what to do.
But instead, I can put it all inside of these script tags.
And so this looks, in between the script tags here, a little more complicated,
but let's take it one step at a time and see if we
can understand what's happening here.
So up on line 6, we have document.addEventListener.
So we haven't used addEventListener before,
but we have talked about the idea of events,
that when something is clicked on, that's
an event; when you mouse over something, that's an event.
It turns out that the document, the web page,
has an event that almost all modern web browsers
support called DOMContentLoaded.
And that event is the event that's triggered when the DOM, the Document
Object Model, that structure of all of the HTML tags inside of our page,
is done being loaded by the web browser.
And so when that is done being loaded, when we've loaded the web browser,
now we can start to do some interesting work.
And so what we've done is we've called this addEventListener function, which
really takes two arguments.
It takes, as its first argument, the name of the event
that we want to listen for--
in this case, the DOMContentLoaded event.
But the second event is actually a function.
And so JavaScript allows us to do what are
called higher order functions, where we can
treat functions themselves as values.
And if we want to take a function and pass it into an argument
to another function, the same way we would
pass an integer into a function or a string into a function, for example,
JavaScript allows us to do that.
Python allows us to do it too, and it's a feature of many modern programming
languages nowadays.
But we can take this whole function that starts with function
and is enclosed inside of these curly braces,
and pass that whole function in as the second argument
to this addEventListener function that we're calling here.
And this parenthetical at the end is closing the argument list
to addEventListener.
And so what does this all mean?
It means that we're telling the document that when the content of the DOM
is loaded, this is the function that should run.
It's not going to run right away.
It's what we might call a callback function.
When the content of the DOM is loaded, then JavaScript
is going to call this function and it's going
to run the contents of that function only when
that event listener is done running.
So what are we saying here?
Inside, when the DOM is done running, we're going to document .querySelector
button--
in other words, extract the button from the page.
There's only one in this case.
But as was mentioned before, if there had been multiple buttons,
querySelector would just select the first one.
And then we're setting the onclick attribute of the button.
This is effectively the same thing as what
we were doing before, where we actually had the button tag and we said,
onclick equals--
here's what to do.
And we're setting onclick equal to count,
where count is also itself a function that's defined down here.
And this is the same function that we've been
looking at before that just increments the counter,
updates the HTML of the heading, and then has this if condition that
alerts or doesn't alert.
But we're taking that function and treating it
like a value, which JavaScript allows us to do.
And we're saying, when the button is clicked on, what should you do?
Run the count function.
Again, that count function won't run right away.
It will only run when that button is clicked on.
So questions about these three lines of code
in particular that are sort of strange to wrap your mind around at first--
treating functions the same way you would treat any other values,
like numbers or strings.
But ultimately, this is one of the most powerful features of JavaScript,
which is the ability to take functions and pass them
as arguments into other functions, and to set them as callback functions that
only run when certain things happen.
Yes?
AUDIENCE: Does that mean we can't create a variable called
count in the function [INAUDIBLE],, if we just
try to create a variable without [INAUDIBLE]??
SPEAKER 1: If you tried to create a variable called count,
you would likely get namespace collision from that case, in which case
you would try to avoid that.
Yeah.
AUDIENCE: Why is this better than just explicitly putting
the count function as the onclick attribute when you create the button
[INAUDIBLE]?
SPEAKER 1: Great question.
Why is this any better than just putting the onclick inside of the button
inside of the HTML, which seemed a whole lot simpler as opposed
to this complexity of DOMContentLoaded and such.
As sites start to get more and more complex,
you'll see added value that we can get out of this.
For example, if we had 100 buttons and we
wanted all 100 buttons to do something in particular when they're clicked on,
we could go through all 100 buttons in our HTML
and add onclick attributes to all of them.
Or we could, in JavaScript, do things a little more programmatically,
and try and select all of the buttons, and give them
all an onclick attribute in some loop, for example,
which would be slightly cleaner in terms of using programming
structures like loops and conditions in order
to modify the attributes of the events.
And in other cases, you might want a little more control over what
happens when you click on a button.
You might want a button to do one thing in certain situations
and another thing in other situations.
And being able to programmatically say "if something is true,
then set the onclick attribute to count; and if it's not true,
then something else" gives us a little more flexibility there.
And finally, it's just a little bit cleaner.
That inside of the body of the HTML now, we no longer have any JavaScript code.
So if I need to change anything in terms of just the JavaScript,
I only need to change what's inside the contents of the script tags.
And we'll see another example of factoring this
out even more in just a moment.
But good question.
Was there another question someone had?
All right.
So this was just allowing us to add an event listener
for when the DOM is done loading.
And we'll see this syntax quite a lot in the examples today,
where when the DOM is finished loading, then we want a particular code to run.
So this is an example of us trying to factor out some of the JavaScript code
from the HTML that's down below into this script tag.
But what we might like to do is factor this out even more
and put the JavaScript code not just at the top of the HTML file,
but in a second file altogether.
Now, aside from just cleanliness and moving things
out into separate places, what might be a reason why
we might want JavaScript code factored out
into an external file instead of inside of the same file as everything else?
Yeah?
AUDIENCE: You can reuse it.
SPEAKER 1: It's reusable.
Great.
If I had multiple HTML files that wanted to use the same JavaScript,
I could just reference them all to the same JavaScript
file in much the same way that, when we were designing style sheets in CSS,
we could factor them out into a separate CSS
file such that multiple different pages could use the same CSS style sheet.
And so what would this look like to try and factor out
our JavaScript into a separate file?
Let's take a look now at counter3.html.
And this is the same thing, except instead
of something inside of the script tags, I've just said,
script src equals counter3.js.
counter3.js is the JavaScript file that contains all the JavaScript code
that I care about.
And in the body, I just have a heading and a button.
But inside of counter3.js, this is all the same JavaScript as before.
Add the addEventListener for when the DOM content loads, which adds
count to be what happens when the button is clicked on.
And then I have the count function here, which actually does the incrementing,
and the query selecting, and the alerting.
And so this allows us to factor that JavaScript out even more,
helps to clean up the contents of the HTML page.
It also makes it reusable, as was just mentioned.
And so these are more examples of how we can begin to clean up the code
that we've been writing.
Questions about anything so far?
OK.
I'll talk briefly about variables just for a moment.
The latest versions of JavaScript have a bunch of different ways
to define variables.
And we've seen the example of let counter equal 0
to be one example of how variables might be defined in JavaScript.
But there are actually multiple ways in which we can use variables.
And there are three that are the most common--
const, and let, and var.
And so we've seen let in the example that we just used.
But I'll talk about the distinction between these three.
const is the variable type that means a constant value,
as you might guess from the name.
And so if I define a variable as a const variable,
then I can't reassign it later.
If I try to reassign it to some other thing later on in the JavaScript page,
it's going to give me some kind of error.
And so it's useful if I want to define a variable
that I know is not going to change.
And it can help to avoid potential bugs later
on down the line where I might try to change it
even though it shouldn't be changed.
And so this just helps to enforce that that variable's value will not change.
let is the example of the variable that we've
been using so far, where let will exist inside
of the scope of the innermost curly braces surrounding it.
And you'll see an example of that in a moment.
But in particular, if you define a variable with let inside
of a block of curly braces, then outside of that block of curly braces,
that variable will no longer exist.
As opposed to var, which operates similarly
to let but slightly differently.
If you define a variable as var, then it will exist all the way inside
of whatever function it was originally defined
in if it was defined inside of a function
even if it was included in an enclosing set of curly braces.
And so that was sort of a lot of complexities and nuances
between the three.
We'll take a look at some examples of those variables actually in action
now, so that you can get a sense for when
you might use one of these variable types over another
and how their behavior differs a little bit.
So let's take a look at variable0.html.
And so all variable0.html does is if true--
so if true is true, which is always true,
so this condition will always run--
we set var message equal to Hello!
And then we alert with that particular message.
And because it's defined as a var, then it's
going to exist even outside of just the contents of that if condition.
And so if I now open up variable0.html, I get an alert and this page
says hello.
So it behaves as we might want the page to behave, because I define it as a var
and even though it's inside this span of curly braces, when I alert,
it alerts with the correct value of message.
variable1.html instead is going to use let instead of var.
So the only change here is that instead of var message
equals Hello!, I've said, let message equal Hello!
And then down below, I get an alert that says alert message.
Any guesses as to what might happen now if I try to open variables1.html?
A blank alert.
That's a good guess.
Other thoughts?
An error.
All right, let's try it out.
Let's see what happens.
Let's open up variables1.html.
And it just says "Welcome!" and I don't see anything so far.
And I also don't see an error message just yet.
And so one thing to look for if the page doesn't
seem to be doing what you might initially think it might be doing
is to actually look at the JavaScript console, which
is built into most modern web browsers, and just take a look
and see if there actually were any errors.
And so an easy way to get to that, in Google Chrome at least,
is to Ctrl-click on the page and go to Inspect, which opens up the Inspector.
And if I click over here now to the console, what I get
is the actual JavaScript error.
And so this is where JavaScript errors are
going to appear if I'm running them in the web browser,
because all of the JavaScript code is running inside the web browser,
inside of Google Chrome.
And so any errors that pop up are going to be part of--
they're going to show up inside of Google Chrome.
And so this says, "uncaught reference error,
message is not defined at variables1.html:11," meaning line 11.
And so if I go back to variables1.html, line 11, this is that line.
And the reason why message is no longer defined
is because let only defines the variable in the scope
of the outermost set of curly braces.
So this is very similar to how programming languages like C
treat their variables, where the variables are only
existing inside of the scope in which they're
defined based on those curly braces.
And finally, we'll take a look at one other example.
We'll look at variables2.html, where, in this case,
we have const message equals Hello!
And now I'm trying to reset the value of message.
Message equals Goodbye!
And then I'm going to alert that message.
Guesses as to what might happen?
Plenty of reasonable guesses here, but let's--
what do we think?
AUDIENCE: Another error.
SPEAKER 1: You could get another error.
And in fact, that's exactly what's going to happen.
We see "Welcome!"
We don't see any error message.
But if I go ahead and inspect this and go to the console again,
we see "uncaught type error, assignment to constant variable
at variables2.html, line 8."
And so we're not allowed to take a const variable
and reassign it to something else.
And so here on line 8, trying to set message
to be "Goodbye!" before we alert it is not going to be something
that JavaScript allows us to do, whereas if we had defined a variable with
let or var, then we could have.
So three slightly different ways of defining
variables that result in slightly different behavior.
It's good just to understand all three and to know how
they behave in different circumstances.
And those are just different ways of how we might
go about using variables in our code.
Yeah?
AUDIENCE: Is there a default if you don't say what it is?
SPEAKER 1: If you don't specify what it is,
I believe it will default to binding as if it were a var,
but I'm not 100% sure about that.
I'll have to check on that.
Good question.
Other things?
OK.
One other interesting thing to note about this JavaScript console
that we're looking at here inside the web browser
is that it actually allows us to execute JavaScript code in much the same way
as the Python interpreter allows us to execute code here as well.
So for example, if I wanted to define another variable, like let x equal 28,
I can just type JavaScript code into the console here.
And if I type "x" now, for instance, that's going to be equal to 28.
And that's something that you can do inside of this JavaScript console.
So if you're ever confused as to what exactly is going on here,
oftentimes it can be a good idea to use the JavaScript console, run
some JavaScript code in order to get a sense for what's really happening.
For example, if I were to run document.querySelector,
h1, then what I get back is it'll actually
show me what happens when I run document.querySelector h1.
Well, it gets me that h1, "Welcome," exclamation point, end the h1 tag.
And so you can see what the actual result of running
some of this JavaScript code would be.
And likewise, if I tried to run document.querySelector, h1,
.innerHTML to see what's the inner HTML contents of it,
then I get just that string, "Welcome!" as the result of that.
And so this can often be a helpful debugging tool
if you're trying to understand what's going on inside of your JavaScript
code, where you can actually run JavaScript inside of the console
in order to manipulate things and change things.
And one nice example of that, actually, is
if I go back to our counter one example, this example where
I could click here and just increment the counter again and again
and again, if I wanted to, I could go here and say,
what is the value of this variable counter?
It's currently 7.
If I set counter equal to 27 instead, what's
going to happen when I click here?
28.
Exactly.
Because I've reset the value of counter, you
can actually manipulate the values of variables inside of the JavaScript
console and make manipulations however you like them.
And you can see the results actually appear on the web page.
I can do document.querySelector, h1 to get at that counter.
And if I set the innerHTML to be "Hello," well, now
I've just changed the contents of the web page to be, instead of 28,
now it says "Hello!"
But as soon as I click here again, it increments the counter
and resets the contents.
So this allows you to manipulate web pages using JavaScript, using
the console in that way as well.
Questions about anything we've gone over so far?
OK.
Let's take a look at a couple other more common use cases
that we might find with regards to JavaScript.
Let's take a look now at hello1.html.
So we saw an example of an alert that just said "Hello!"
Let's take that to another level now.
Inside the body of our web page in hello1.html,
we have a form whose ID is form just to make it easy to reference later.
And I have an input field.
This ID is name-- again, just to make it easy to reference later.
And the placeholder that we're going to fill into the input field
is just going to be the word name.
And it's going to be an input field where I can type text.
And underneath that, I'm going to have an input whose type is submit.
That's just going to be a button that allows me to submit the form.
So now what is going on inside of my JavaScript?
Well, when the DOM content is loaded, I'm going to run this function.
And what am I doing here?
I'm running document.querySelector #form .onsubmit.
Onsubmit is yet another event that we've been looking at.
We thought when something is clicked on, that's an event.
When something's moused over, that's an event.
When the DOM content is loaded, that's an event.
When a form is submitted, that's also an event.
And so what's going to happen when this form is submitted?
We're going to run this function, again, passing this function in
to be the value of this onsubmit attribute of my form.
And I'm defining name as a const, because it's not going to change,
equal to document.querySelector #name .value.
Any guesses as to what that's going to extract?
What is querySelector #name .value?
Yeah?
Great.
It's going to be whatever the user typed into that text box,
because that text box had an ID of name.
And so if I querySelect for #name, that's
going to get me just that HTML input field.
And if I get the value of an input field, the value of the input field
is whatever it is the user was typing in to that input field.
And then I'm going to alert.
This is, again, one of those template literals,
those formatted strings, where I'm saying "hello"
and then plugging in the value of that name variable there.
And so I'm going to say hello to whoever typed in their name.
So if I go back to the actual page and open up hello1.html,
if I type in "Brian" and press Submit, it says "Hello, Brian."
If I instead type "Meredith," for example, and press Submit,
then it says, "Hello, Meredith."
And so I'm able to use the contents of the value of the input field
to dynamically create whatever alerts that I wanted to create, in this case.
But I could have done anything with this name variable.
I'm just using JavaScript now to extract information out of the form.
Questions about that so far?
Yeah.
AUDIENCE: So you call it a constant?
But then you're able to do it twice.
Isn't that changing your constant?
SPEAKER 1: Great question.
So it was a const variable, but I was able to use it twice, right?
I typed in my name and it said "hello, Brian,"
and then I typed in Meredith's name and then it said "hello, Meredith."
What's going on there?
Why did it let me do that?
Any guesses?
Well, one is that the page reload.
Yes.
Certainly.
Because when the form was submitted, it ended up going to the page again.
But another thing to bear in mind is that const here
is located inside of the scope of the function, which becomes relevant
as well.
All right.
Let's take a look at--
so we saw now that we can use JavaScript to be able to access
the HTML contents of the page.
Now let's take a look at using JavaScript to modify
the styling of the page as well.
So let's take a look at colors0.html.
I'll show you first what color0.html is going to do.
This web page says "Hello!"
And it's got two buttons at the bottom--
Red, Blue, and Green.
And when I click on those buttons, Red changes "Hello!"
to red, Blue changes "Hello!" to be in blue, Green changes it to be in green.
And so how is that working in terms of the code?
Well, here in the body of the page, I have a heading that says "Hello,"
then a button whose ID is red, which just says Red;
a button whose ID is blue, and that button's labeled Blue;
and a button whose ID is green, and that button is just labeled Green.
And now what's here inside of the script tag?
Well, I have document.addEventListener DOMContentLoaded.
So here is the code that is going to run when
the DOM is done loading when we're ready to actually do interesting things
with the content of the site.
I say, document.querySelector #red onclick,
meaning when the Red button is clicked, this is the code that should run,
this following function.
And so inside of this function, we have document.querySelector #hello
getting that thing with ID hello, which was that heading that says hello.
.style gets at its style attribute.
And then I can modify any of the CSS properties that I had.
Remember that we had CSS properties for the color of something,
or the font size of something, or the background for something.
And any of those CSS properties I can now modify by accessing this .style
attribute of the HTML element.
So I'm saying .style .color.
That's going to be equal to red.
And likewise, I'm doing the same thing for when I click on Blue.
Then I change the color of the "Hello!" element to be blue.
And likewise, when I click on Green, I change the color of that to be green.
Questions about how that was able to work?
Anyone see areas for improvement in this code?
Something that might look suboptimal, at least at first glance?
Yeah.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Yeah, I have three functions that are all really
doing the same thing.
I have a Red button that changes the "Hello!" to be red instead of black
or whatever it was originally; a Blue button
that does the same thing; Green that does the same thing.
And this is happening because querySelector, as we've seen it so far,
really only allows us to extract the first element that it finds.
So I'm extracting the Red button and then doing something with,
extracting the Blue button and doing something with that.
And these functions are slightly different.
And so what I'd like is for some way to generalize this,
to be able to only need to write this code once, and then have it apply
to all of these different buttons.
And so there are a number of different ways to do that,
but we'll take a look at one way right now.
And so here what we have inside of the DOM Content Loaded section is,
instead of document.querySelector, I have document.querySelectorAll.
And what querySelectorAll is going to do is instead of just selecting
the first thing that matched, as querySelector did,
querySelectorAll is going to return to me an array of all of the things
that matched the query that I was trying to perform.
And so here, I'm querying for anything that has the class Color-Change.
In particular, as we'll see down below in the HTML contents
of the page in just a moment, I've given all of these buttons
the class Color-Change.
And as a result, when I querySelectorAll for anything
with the class Color-Change, the result is
that I'm going to get back an array of all of those different buttons.
Then after that, I have this .ForEach.
And so ForEach is a built-in function that we
can use on JavaScript arrays that allows me to run a function on each element
inside of that array.
And so ForEach is another example of a higher order function.
ForEach is a function that is taking, as input, yet another function that I
want to do something interesting with.
And it's a function that is going to take,
as input, one particular element of that array.
And so before we go on to seeing what that function actually does,
let me show you the HTML content of this page.
So inside of my body, I have this heading that says "Hello!"
I have three buttons.
All of them have this class, Color-Change,
that is going to be the class that allows me to select all of them
at once.
By selecting everything that has the class Color-Change,
I can select all three of those different buttons.
And then what you'll find is I've added this data-color attribute
to all of my buttons.
Now, data-color is not just some attribute
that is built into all buttons, but rather it's
an example of a data attribute where, in HTML oftentimes, if I
want to associate additional data with an HTML element
that's not displayed on the page, I can put it
inside of a data attribute with the name of my own choosing
so long as it begins with data, hyphen, something in order to give that HTML
element a little more information about itself
or information that it would need to know for purposes of being used later,
oftentimes in JavaScript.
And so what I've done here is I've defined these buttons
that all have class Color-Change, but they all
have this color data attribute, data-color, which is equal to the color
that they should try to turn the text into.
So this one has data-color red, the Blue button has data-color blue,
the Green button has data-color green.
And how do I actually use these data attributes?
Well, if we look back up here at the JavaScript code,
when I do this querySelectorAll selecting for all of the buttons,
or all of the HTML elements that have the class Color-Change, I'm saying,
ForEach--
in other words, run this function on each one of these individual buttons--
here is that function.
That function takes, as input, one of those buttons.
It's going to first operate on the first button, then the second button,
and then the third button.
And we're setting the button.onclick property of each
to be this new function.
So this is the function that should run when we click on that button.
document.querySelector #hello .style .color is the same thing we've seen
before.
We're going to take that heading, that "Hello!" heading,
get at its style attributes, and change the color of its styling.
And in particular, what color do we want to change its styling to?
Well, we want to change it to whatever that data-color attribute
of the button itself was.
And so we have access to this variable button,
because the ForEach function will loop over each element in the array
and let us run a function, passing in just an individual element as input.
And we called that button.
And in JavaScript, to access any of the data attributes,
we use the .dataset property.
And so by going to button.dataset.color, what that's going to do is it's going
to take that button, extract its data attributes-- in this case, data-color--
and get at one particular attribute.
In this case, we want the color attribute.
And so the long story short of what all of this is doing
is we're selecting for all of the different elements that
have the Color-Change cloths, which is all the buttons.
And for each one of those, we're going to run this function that
sets the onclick property of that button to be
a function that takes the "Hello!"
heading and changes its color to be whatever the data-color attribute
of that button originally was.
And the result of this is that colors1 behaves exactly the same way as color0.
They have these Red, Blue, and Green buttons.
And when I click on individual ones of them,
it causes the color of that "Hello!"
heading to change all based on this dataset property.
And we were able to do that now using just a single function that
changes the color as opposed to color0, where
we needed three different functions, one for each of those individual colors.
Questions about any of the stuff that was happening here?
So this is a lot of new syntax.
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Good question.
So what happens if we try to inspect one of these buttons inside of the Chrome
Inspector?
So I can do document.querySelector.
And I'm just going to select for just an individual button for now.
And remember, the querySelector only returns one thing.
So even though there are three buttons here inside of the HTML document,
running querySelector on button is just going
to return to me the first button, the Red button.
And if I do onclick, what I get back is that function.
This document.querySelector #hello is equal to whatever the button's data set
color is, because I've updated the onclick attribute of that button
to be that new function.
Great question.
Other things?
All right.
So inside of colors1.html, what you've likely noticed now
is that we have a lot of these different functions.
Here's a function that doesn't take any parameters.
Here's another one that doesn't take any parameters.
Here's one that takes button as a parameter.
Functions are incredibly common in JavaScript,
as we've seen already, because we have these callback functions that
are called whenever events are fired.
And in the latest versions of JavaScript, ES6 in particular,
they've introduced a new syntax for functions
that makes things just a little bit simpler to deal with.
And so I'll introduce that syntax now, and we'll
use it for the remainder of the lecture today just to give you a taste for it.
So inside of colors2.html, which is going to do exactly the same thing,
we're going to introduce this arrow notation syntax.
And let me briefly talk about what that arrow notation
syntax looks like to define what are called arrow functions.
And then we'll take a closer look at them, actually, in practice.
And so this is an example of an arrow function that takes no arguments.
And so in particular, an arrow function is defined without the use of the word
"function."
And instead, it just has any arguments the function takes,
with the just empty parentheses meaning no arguments, followed by equal sign,
arrow just to be an arrow, meaning, take this as input and then run this code.
And so this is just ES6's way of defining functions.
You can use functions the old way just as well.
And these functions behave very slightly differently,
as we'll see later on today, but are, in fact,
just a more succinct, more compact way of writing functions in ES6.
And so this is a function that takes no arguments
and just alerts "Hello, world!"
in much the same way as that hello function
that we saw earlier this evening did.
And this, for example, is a function that takes, as input, a variable
x as its input and then, in the body of the function, alerts whatever
the contents of x was, for example.
And you can see these functions represented even more succinctly.
If there's a function that's just taking input and returning a value almost
right away, you can, in fact, represent a function as simply as this,
where you have an x, arrow, x times 2.
This is a function defined in ES6 that takes, as input,
a number x and returns whatever x times 2 is.
And no curly braces required in that case either.
So these are just more succinct ways of representing functions.
If you take a look at modern JavaScript code,
you'll see syntax like this a lot.
So I just wanted to introduce it to you to give you a feel for what it is
and how it works.
And so if we were to take that color example
and rewrite it using arrow functions, it would just look something like this.
Instead of after DOMContentLoaded function and then the curly braces,
we would just say, parentheses, meaning this takes no arguments;
followed by equal sign, arrow; and then the contents of what
should happen when the DOM contents is loaded.
In the example in the old example, when we
were running that ForEach function and we had a function that took,
as input, the button, and then ran all of this code in here,
in the new example with arrow functions, we would write that as button--
that's the input-- then arrow.
So button is the input.
And then this is what should happen in the body of that function.
When the button is clicked, then run a new function that takes no arguments.
And then runs this code as a result. So colors1 and colors2 are effectively
doing the same thing here.
We're just using the new ES6 arrow function notation
to achieve the same thing in just a slightly more concise way.
And it's just good to see examples of both,
so that you know that both are perfectly reasonable ways of writing
these functions.
Questions about anything so far?
OK.
We'll take a look at one last example before we take a short break.
We're going to take a look at colors3.
And so in colors3, what you'll notice is--
I'll show you how it works first.
It says "Hello!"
And instead of three buttons, we have a dropdown box.
It starts off as black.
We also have red, blue, and green.
And what this dropdown box will do is whenever I select a new color--
I select red, for instance-- then it changes the contents to red.
Blue changes it to blue.
Green changes it to green.
So slightly different.
But we'll see how that's implemented as well just to give you another example.
So in colors3.html, I have a heading.
It just says "Hello!"
Same as before.
Instead of buttons, I have a select dropdown.
Select id is color-change.
And I have all these individual options.
Each option is an option in that dropdown list.
What's enclosed in between the option tags
is the actual text of the option, which would show up to the user.
Each one also has a value.
And that value I'm going to use JavaScript in order to extract.
So the value of each is just whatever the actual string
of the color that I want to change "Hello!" to be in is going to be.
And now inside of the script contents of the page,
when the page is done loading, here's what should run.
Document.querySelector color-change.
So color-change is that select dropdown.
And onchange, as you might guess, is yet another event.
So onchange is the event that gets fired when I change my selection
in the dropdown box of the Select menu.
And when that happens, I'm going to run this function, which gets at "Hello!"
and change style.color to be this .value.
And so the only really new thing here, aside from the onchange event,
which should hopefully be pretty straightforward
if you've seen examples of other events already, is this keyword "this."
And what does "this" refer to?
Well, "this" will, generally speaking, refer to whatever value
this function is being operated upon.
And so in this case, "this" refers to document.querySelector color-change--
in other words, what I got when I extracted out
that color-change ID, because I'm setting this function
to be this onchange function, what happens when
I change the color-change dropdown.
And so this is whatever that select dropdown is.
And so if I take that select dropdown and get at its value,
that will be whatever the color is.
And this is a little more sophisticated than you'll need to really worry about
for purposes of just this class.
But the way "this" knows what value it should be bound to, so to speak,
what value "this" should take on is based
on the function in which it is defined.
And the long story short of it is that "this" will only be bound to the thing
that you're calling the function on if you are using functions
in the traditional functions without actually explicitly writing
the word "function."
If you use arrow functions, then "this" will instead
be bound to whatever "this" was bound to in the code that
was enclosing that arrow function.
So long story short, doing "this," for instance,
would not produce the same effects, because "this" would no longer
be bound to the color-change dropdown.
It's this keyword function and defining a function
in the traditional way that allows "this" to be bound to the thing
that I'm actually trying to modify.
And so as you go about experimenting with functions
and maybe writing some functions this way and some using arrow functions,
you may run into being tripped up by when "this" takes
on one value as opposed to another.
A good way to begin to debug this, again, is just to use the debugger.
And we'll see more examples of that later.
But for now, we'll take a short break.
And when we come back, we'll take a look at more examples of using JavaScript.
And then we'll integrate JavaScript back in with Python
to build dynamic web applications in Flask,
but use both Python and JavaScript as well.
But all that in a couple minutes.
OK.
Welcome back.
So so far, we've been taking a look at JavaScript,
looking at ways that we can use it in order
to manipulate the DOM by editing elements that exist there already
or by reading elements and values from dropdown boxes and input fields
in order to do interesting and useful things.
What we'll take a look at now is yet another example
of how we can actually add things to the DOM,
how we can add to the contents of the web page,
adding elements that weren't there before in order to do
interesting or useful things as well.
And so I'll show you what we're going to try to build up towards,
and then we'll look at the code that'll actually allow us to do that.
So open up tasks0.html.
And what tasks0.html is is it's a simple to-do list application,
where I can keep track of different tasks that I need to perform.
So here's a thing to do.
And when I click Submit, it adds "thing to do" as one of my tasks.
And I add "another thing," and I click Submit.
There's another thing that I need to do.
And it will just continue to maintain this ever-growing list of tasks
that I need to complete based on what I type into the New Task input
field and then clicking on the Submit button to add that to the list of tasks
that I need to perform.
And all of this is just happening using JavaScript.
So let's take a look at the code that we would
need in order to make that happen.
Inside the body of the HTML so far, we have
a heading that just says "Tasks" at the top, then a UL,
or an unordered list, whose ID is task, just so we can reference it later.
And to begin with, that unordered list is empty.
It has no list items inside of it, which seems sort of strange,
because in all of the unordered lists or ordered
lists we've seen before, they've had within them
li, list item elements, that define the contents of the actual list.
But to begin with, we have no tasks to start out with,
and so we need to start with an empty list
and wait for things to be added before we can populate that unordered list.
And then I have a form, whose ID is new-task.
And inside that form, I have two parts to the form.
One is just an input field.
Its type is text, because I'm going to be typing in the task.
Its placeholder is just New Task.
And then I have this submit input field, which is just going to be that button.
When I click on it, it will cause the task to be submitted.
So what is the actual JavaScript code now that makes all of this happen?
So when the DOM content is loaded, here is what I'm ultimately doing.
Let's take a look at this code.
So document.querySelector New-Task.
New-Task is the ID of that form, the form that contains the input
field and the button for submission.
When I submit the form, here is the function
that should run-- all of this code enclosed in curly braces.
I'm creating a constant variable, a constant inside of this function,
at least-- it's not going to change--
called li.
And I'm setting that equal to document.createElement li.
So I'm creating a new element, and the tag of that element
is going to be an li tag, a list item tag.
And then I'm setting li's inner HTML to be whatever the task's value is.
Remember, the task here is the ID of that input field.
And extracting its value will get at to mean all of the text
that the user typed into that input field.
And now, once I have this list item-- it's a new list item element and its
inner HTML is whatever was inside that task--
I need to now actually take that new element
and put it into the website somewhere, because I've
created the element in the document, but I haven't yet
told JavaScript where inside the DOM tree, where inside the page should
this new list element go.
And so what I'll do is document.querySelector Tasks,
getting the thing with ID Tasks, which is that unordered list.
And append to it.
And these HTML elements have all sorts of properties
that you can look up in terms of what they do.
Append just happens to be one that adds a new thing to the end of it.
And I can append to it li, which is this variable that I created here.
And that's going to actually add the new item to the task list.
And afterwards, we're going to do a couple other things.
We're going to select that Task input field, set
its value to the empty string.
And so all that's going to do is it's going
to take the input field that I started out with,
and it's just going to clear it.
So that I've already typed the task, the input field
should clear now, because I've already submitted it.
And then return false.
So normally when you submit a form, the default behavior for submitting a form
is that I'm submitting a form to some other website
and it's going to try and load that new website.
And if I don't specify, it's going to try
and reload the same page again by trying to submit the form to it.
return false stops the form from submitting to some other website,
from trying to reload some new page, because I want to stay on the same page
when I'm done submitting this form.
And so we create the new element, we set its contents
to be whatever the task is, add it to my list of tasks, clear the input field,
and then stop the form, ultimately, from submitting.
And that's what allows us to get this behavior, where we have a list of tasks
and I can add more tasks to it.
Now, there are certainly areas where I could improve upon this application.
Right now if I just click Submit over and over,
I can keep submitting blank elements again and again.
So maybe I'd like to prevent that from happening.
I'd like to make it so that I can't submit anything unless I've actually
typed text into this New Task field.
And so how might I go about doing that?
Let's take a look now at tasks1.html, which is similar
but we've added some additional JavaScript code.
So up here, the first thing that I do is get the Submit button--
whose ID is Submit, I've given it the ID Submit--
and I'm setting one of its HTML properties.
I can edit any of the HTML properties of an HTML element using JavaScript.
And one property that's going to prove particularly useful
here is the disabled property of a button
that just determines whether or not that button is disabled or not,
whether or not I can click on it.
And so I'm setting the disabled property to true.
And now I have document.querySelector Task--
again, that's the input field--
onkeyup.
When I press a key, when I lift that key up,
I've typed in text now, and so what should happen when I lift up that key?
Well, now I've typed in text into the input field,
and so the Submit field's disabled field can now be false.
Because I don't want the Submit button to be disabled.
And then onsubmit works effectively the same way.
When I actually click the Submit button, I still
want to add that new task to my list of tasks.
But in particular, in addition to clearing the input field,
I also want to disable the button again.
So I querySelect for the button, which has ID submit.
And I set its disabled property to be true instead of false.
And so the result of that is that I have this list of tasks.
I can start typing in one task here.
And notice that the Submit button is disabled.
I can't click the Submit button and submit an empty task.
And it's not until I type in a second task
that I can actually click on the Submit button and submit yet another task.
So that allows me to manipulate the HTML attributes of the Submit button,
setting its disabled property to true or false
depending on what I want it to be.
Anyone spot a potential bug with my implementation?
An area where that might go wrong?
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Great question.
If I-- or great answer.
If I type something, some text, and then I erase it,
and now I've gone back to the empty task field,
now the Submit button is still active, and I can still submit a task.
Why was I allowed to do that?
Yeah, because I made a keystroke.
And technically, hitting the Delete button is still a keystroke.
And when I lift the Delete button, that's
a keyup, as well as all the other characters that I was typing before it.
So I'm going to need something a little more robust in order
to allow myself to deal with this situation.
And so we'll go ahead and fix this bug inside of tasks2.html.
And so here, onkeyup.
When I lift up the key, when I type in a new key into the input field,
I went to check.
Let me query for the task input field, get at its adds value,
and I can use the .length property of a string in JavaScript that just gets me
the length of how long a string is.
And if the length of the string is greater than 0-- in other words,
there are more than zero characters inside the input field--
then yeah, I don't want the button to be disabled.
So disabled equals false.
Otherwise, if there were zero characters inside the input field,
then I want the disabled field to be true instead.
And so the result of that is that the Submit button is disabled.
I can add one task.
It's disabled again.
If I start typing in a second task but then delete, delete, delete,
delete, once I empty out the whole thing,
now the Submit button is disabled again.
It's enabled here, and now it's disabled.
And so this allows me to dynamically, using conditions and using
event handlers, begin to make my user interface better and better
by making it dynamic, making it responsive to what
it is that the user is actually doing.
Questions about anything so far?
So a couple of people have asked about jQuery, which
is a library you may be familiar with.
We're not going to talk about it at all in today's lecture,
but jQuery just happens to be a JavaScript library that
is commonly used in order to do similar things,
so the things we've been seeing so far in terms
of selecting, and updating HTML elements,
and updating, and modifying the DOM.
Ultimately, jQuery's become slightly less
popular in recent years as JavaScript itself has become more
and more powerful without the need to use an external library like jQuery.
Using things like querySelector and querySelectorAll,
you can do much of the same stuff that jQuery
was good with relative efficiency.
And so jQuery, as a relatively large library that takes time in order
to load and might slow things down a little bit,
is becoming slightly less popular, although you will still
see it in many instances.
In fact, if you are using Bootstrap for any of your projects, for example,
and are using any of Bootstrap's JavaScript features,
Bootstrap is still using jQuery as a library in order
to do most of its JavaScript work.
And so you might stumble across jQuery-like syntax
if you're looking at either Bootstrap's JavaScript or JavaScript
on some other web pages or other libraries.
And so just wanted to make you all aware of what
jQuery is even though we're not going to look at it too much in today's lecture.
We'll take a look, though, at a couple other features
that are built into JavaScript itself.
In particular, let's take a look at interval.html.
And so interval.html, inside the body, just has a heading that says counter.
There's no longer a button.
But let's take a look at what's happening inside of the actual script
contents.
When the DOM content is loaded, I'm calling the setInterval function,
a function built into JavaScript.
And the setInterval function takes two arguments.
The first is another function--
in this case, count, which is a function.
And the second is the interval at which I should call it.
And so setInterval count 1,000 is saying, call this count function
every 1,000 milliseconds, every second.
And the count function is the same as the count function we've seen before.
It takes this counter value and it increments it every time.
And so the result of this is that without needing
to click on any buttons, if I open up interval.html, it'll start at zero
and will count one, two, three, four, counting one number per second
without my needing to click on any buttons.
JavaScript is just running my count function
one time every 1,000 milliseconds.
And so that can be useful if you want something
happening on a recurring basis.
But another thing you might notice about all of the JavaScript web pages
that we've had so far is that they aren't able to remember anything.
If I'm looking at my website right now, it's
counting 23, 24, 25, and it's continuing counting.
But if it gets to 28, and I close the page,
and then I open interval.html again, for instance, now I'm back to zero.
It reset the entire page because all of that updating of the variables gets
lost when I close that page.
So what are some ways that I can get around that and actually store
information such that if I close the page
and open a page later on today or in the next day, that I can still
retain some information, some memory of some state?
In order to do that, we'll use a feature of JavaScript
that many modern web browsers support called local storage, which
allows us to store things inside of the local machine
such that even if I close the page and reload the page later,
I still have access to that same local storage.
So let's take a look now at storage.html.
So storage has a counter and a button that says Click Here,
much like the examples we've seen that are counting before.
And what's happening inside of it?
And the first thing I'm doing is I'm doing localStorage.getItem, counter.
So localStorage is just a variable I have access to.
And it has two functions that I'm likely going
to want to use-- getItem, which tries to get an item for me from local storage;
and setItem, which sets the value of something in local storage.
And so I'm trying to get the item called counter.
And exclamation point just means "not."
So if not getItem counter--
in other words, there was nothing called counter--
I'm going to setItem, localStorage, set it equal to 0.
So I'm setting counter equal to 0.
And now what I'm going to do is initially set the counter's inner HTML
to be whatever the value of the counter variable in localStorage is.
So if localStorage starts off at zero, then the counter
is going to initially display 0.
But if it's something else, it's going to display something else.
And now every time this button is clicked,
querySelector button onclick, what am I going to do?
Well, instead of setting the variable counter to be zero at first,
let's set counter to be whatever localStorage's counter is.
Then I'm going to increment the variable counter.
counter++.
Then I'm going to set the inner HTML of my counter heading to be
whatever that counter variable was.
And then I'm going to setItem--
localStorage.setItem.
Set the value of counter to be whatever my variable counter is equal to.
And you could get around this without even needing the counter variable,
but I'm including all the steps here just to be explicit about it.
And so what's the result of this?
This local storage is a variable I have access to.
Or it's storage that I have access to even when I close the page
and reopen it.
So if I open up storage.html here--
and I have this button that will keep counting.
And I count up to five, for example, or six, seven, eight, nine,
if I now close this page, in the examples
we've seen before, it would reset back to zero when I opened the page.
But now when I open the page, I'm back at nine.
It's retrieved that value from localStorage, and it's using it,
and now I can continue to click and continue
to update the value of the variable in localStorage.
Yeah?
AUDIENCE: So the localStorage is storing it in Chrome?
SPEAKER 1: Yeah, the browsers need to support localStorage,
and so Chrome happens to be a browser that allows us to store information
locally within Chrome.
And most other modern web browsers will also support localStorage now as well.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Yep, it will continue to persist.
And so it's a nice way of being able to store data locally such
that you can reference it later in future uses.
And you may find this proves useful in future projects.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Repeat that.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: You can think of it as--
so a cookie-- so the question is the relationship between this and a cookie.
So a cookie would be with regards to interactions with some kind of server,
that a server might use a cookie in order to remember a particular user.
With regards to this storage website, there is no server involved.
It is just data that is being stored on the computer.
But it's got some similar elements in spirit.
Yeah?
AUDIENCE: How do you avoid [INAUDIBLE] collisions?
Because you just said [INAUDIBLE] that somebody could use
counter in their web page?
SPEAKER 1: Great question.
So the question is, how do you avoid namespace collisions?
What if someone else is also using a localStorage variable named counter?
The way localStorage works is that it is reserved for a particular domain name.
And so if your website, at one domain name,
is using a particular variable in localStorage
and some other website at a different domain
is using a variable in localStorage, those names
will not conflict with each other.
But if you, on your domain on two different pages,
for example, are using localStorage, those
will be the same values for localStorage.
And so it would be your responsibility then
to make sure that those names don't conflict with each other.
Yup.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: It's still going to be 18.
Yeah.
OK.
So those were some other features of JavaScript-- the intervals
and localStorage.
What we're now going to take a look at is taking a step back
to our world of Flask and Python, and looking
at how we can develop applications using Flask
that are able to take advantage of JavaScript to do interesting things.
And so in order to do this, we are going to introduce
the concept of Ajax, which stands for Asynchronous JavaScript and XML, which
you can just think of as a technology that we can use in order
to get more information from a server even without needing
to reload an entire new page.
So if we need to get information from some website or some web server,
and incorporate it into the page that we're already
on without reloading a new page, we can use Ajax in order to do that.
And we'll see an example of that right now.
So what I'm going to go do is go into the Currency example.
And Currency is a Flask program.
So if I take this URL and go here--
we saw in previous examples in past weeks
how we were able to use the Currency API on fixer.io
to get at exchange rates between different currencies.
And we saw a command-line program that allowed us to do that.
Now we're taking this application to the internet.
What I have is a page that just gives me a text field to type in a currency
and a button that says Get Exchange Rate.
And so if I type in "euro," for example, and click Get Exchange Rate,
without any new page--
I didn't go to any new page-- it just tells me,
1 US dollar is equal to this many euros.
And if I do Japanese yen, then I get one US dollar
is equal to 105.65 Japanese yen.
And it's not the case that when I loaded this page
I needed to load all of the different currency exchange rates
in order to load this page.
What's happening, as we'll soon see, is that when
I type "Great British pounds," for example, when I click the Get Exchange
Rate, I'm making an Ajax request.
I'm making another request to my web server,
my Flask web server, which is going to get the exchange
rate for the Great British pound, give it back to me,
and then I'm using JavaScript to update the DOM in order
to render this new content.
And so we'll take a look at how all of that actually works.
So let's take a look at application.pi inside of this Currency application.
Inside of this default route, I'm just returning an index.html template--
same as the Flask programs we've seen before.
And I also have a route called /convert, which takes the request method
of POST--
and this is going to be the route that I'm
going to use when I need to get a currency exchange rate.
And so what's happening inside of this convert function?
Well, I'm first going to get at the data that's associated with this form
submission, in particular getting at whatever the value of a currency form
attribute is, and save that inside of a variable called currency.
And then I'm going to make an API call to the API,
passing in US dollars and the currency that I want to convert to.
And you can take a look at the examples from past weeks
when we were dealing with APIs last time in order
to see in more detail how this API works.
It's exactly the same thing.
I'm just now doing it inside of a Flask application instead of inside
of a command-line application.
And so I'm going to request that new exchange rate.
If, for some reason, the response for that wasn't successful,
the status code was not 200, then I'm going
to return a JSON object that just says, success false,
this request was not successful.
And I want to let my client know that, let them know that, for some reason,
this request was not successful.
Then I'm going to get the JSON data from that request response
and check to make sure that the currency is actually in that response.
And this just has to do with the way that the API is structured.
So this code is going to vary depending on what API you're using.
But the way this API happens to work is that I
need to check to make sure that the currency is
inside of my list of the rates that came back to me.
And if, for some reason, that currency wasn't
in the list of rates that came back to me,
then I'm going to return success is false as well.
But if none of that was true-- if the status code was 200 and the currency is
inside the data that came back to me--
then I'm going to return this JSON object--
success was true and the rate is whatever data rates' currency is.
And if you recall from last time, this is how
we extracted the actual exchange rate.
And so the long story short of what the convert route is doing
is if I provide, as input to this route, a currency value,
like Japanese yen or euro, then it's going
to make an API request to our currency converter API,
and then return back to me, ideally, a JSON
object telling me that the request was successful
and also giving me back a number representing that exchange rate.
It's not giving me back a full HTML page.
You can think of this like an API route that's just giving back to me
a JSON object, telling me whether the request was successful or not,
and telling me what the actual exchange rate is.
And I'm going to use that route now inside of my application.
So what's inside of my application?
Well, inside of index.html, I have a script tag
where I'm including this index.js JavaScript file.
And this URL for static is just Flask's way of incorporating static files.
We've seen a couple examples of this in prior weeks.
And inside the body of this page, I have a form where I can type in a currency
and click a Submit button.
And then I have this div down here, just a section
of the website whose ID is result. This is
where I'm going to put the result of the computation.
But to begin with, I haven't requested a currency yet.
So that result field is blank.
There's nothing there just yet.
And so all of the logic for what's actually going to happen
is going to happen inside of index.js.
And so let's take a look at that now and see what's happening.
So when the DOM content is done being loaded,
here's the code that's going to run.
What I want to happen is I only need to make some sort of interesting request
when someone submits the form.
When they click Get the Exchange Rate, then I want to do something.
So I select for the form, the thing that has ID form.
And when it is submitted, I'm going to run this function.
What's happening inside of this function?
Well, I'm going to define a variable called request, which is a new XML HTTP
request.
This is just an object that I'm creating in JavaScript
that is going to allow me to make an Ajax request, that's
going to allow me to make an HTTP request to some other web server
in order to get some information.
So then I'm going to define a variable called
currency, which is going to be whatever the current value of the Currency input
field is.
Currency, again, is that text input field.
Getting at its value will just get me whatever
it is the user typed in-- either euros, or pounds,
or whatever the currency might be.
And so I now have this variable request that
is going to represent a way to make new requests to other websites,
much like the request library in Python does, although perhaps
a little more wordy, as we'll soon see.
And I have a variable called currency that is that currency.
And now I'm going to say, request.open, meaning, initialize a new request.
It's going to be a POST request, because my route accepts the request method
POST.
And where am I going to request this to?
I need to provide the URL.
And I'm going to hit the route /convert.
And that's taken straight from application.pi,
where we defined a route called /convert and said that it accepts request
methods of POST.
So what happens next?
Well, now I need to tell my request, what should you
do when you're done being loaded?
In other words, when the request is done, what should happen?
And so here's what's happening here.
I'm taking my request variable and setting its onload attribute.
Again, when it's done being loaded--
in other words, when the request is done--
here's the function that should run.
And I'm creating a variable called data.
And what should data be equal to?
Well, ideally, I want data to be equal to that JSON object that got returned
to me when I made a request of a /convert route.
All I have, though, is this request variable.
So what you'll learn is that request.responseText
is an attribute of request that I can use
to get the text of the response, whatever the text that came back
to me when I made the request was.
And JSON.parse is a built-in function in JavaScript that takes that text
and tries to parse it as a JSON object that
lets me access it using keys and values, like we saw in the API lecture
last week.
So now I have this data variable that should be equal to whatever came back
to me from that /convert route.
And if you remember, the /convert route did a couple things.
If the request was successful, then the success key in the JSON object
was true.
Otherwise, that success key was false.
And so here, I'm checking for that.
If data.success-- in other words, if the request was successful,
meaning that success attribute of my JSON object had a value of true--
then here's what should happen.
Well, I'm defining the new variable, contents.
The contents of my result div should be 1 US dollar is equal to--
and then I want to specify what the exchange rate actually is.
And remember, the exchange rate came back to me in this request.
It came back in the response text of the request, which I then
converted into this JSON data value.
And to get at the rate from that data value, I do data.rate.
And so 1 US dollar is equal to, plug in the rate
here, and then plug in the name of the currency, where that currency was
defined in a variable up above, just drawn from whatever it is
the user typed in to that input field.
And so this is going to be what I want to fill in to that result field
inside of my HTML page.
And so now I say, we'll get at that result div
and update the inner HTML to be whatever those contents were.
And so that, if the request was successful,
has the effect of providing, inside that result div, the actual contents
that I care about.
And then finally, else.
If the request was not successful, then I just
want to set the inner HTML to be, there was an error just
to indicate that something went wrong.
Any ideas on what's missing from the request?
Not from the onload function.
But so far, I've created a request.
I said, I want that request to be POST request that goes through /convert.
And this is the code that should run when the request is done being loaded.
What information have I not yet included or used?
Yeah?
AUDIENCE: I didn't see your URL.
SPEAKER 1: The URL is there.
So if you would look a little higher, I specify when I initialized the request
in request.open, we're going to make a POST request to the /convert route
or URL.
But what's still missing?
Any thoughts?
So I'm making a POST request to /convert,
which is going to try and do some currency conversion for me and find
the exchange rate between US dollars and some other exchange rate.
But I haven't yet actually told this request about the currency
that I want information about I haven't told this request, look up
the exchange rate for euros yet.
I've just used this variable currency so far just
inside this contents of what should be displayed on the page,
but I haven't sent that information to the server yet.
So I need some way of taking that currency
and putting it inside of the form data that I want to send to the server.
And so in order to do that, what I'm going to do is create a new variable
called data, which is going to be equal to a new form data object, which
is going to hold the form data.
And I'm going to add to this data, using data.append, a key called currency,
whose value is the currency variable that I defined up above,
that currency variable that was the name of the currency.
Finally, finally, I do request.send, and I send this data along with it.
I want to send a request to the /convert route,
but I want to pass in the data about what currency I actually care about
converting to.
And then return false just stops the page
from reloading after I submit the form.
And so high-level overview of all of this.
And the source code we made available afterwards
if you want to take a closer look at it.
We create a new request; tell it where to request to, the /convert route;
tell it what to do when the request is done,
getting at that JSON data and doing something with that data; then finally,
specifying what information should be included with the submission
of the form; and then finally, actually making the request down here.
And so the result of all of that is that I can type in something like "euro"
and get the exchange rate for a euro.
And if I type in a currency that doesn't exist and try and get the exchange
rate, I get "There was an error" coming back to me all on the same page,
all by making Ajax requests that, without needing to reload the page,
allow me to get additional information.
Yeah.
AUDIENCE: [INAUDIBLE]
SPEAKER 1: Great question.
So the question is, could I have instead just set up an Ajax request that
was directly accessing the currency?
Absolutely.
You could have just requested the API directly and parsed that JSON response,
but this was just meant to be an example of how you can use a Flask server back
end if you have your own data that you want to process in some way
in order to return.
But yeah, good question.
All right.
We'll take a look at one other feature of JavaScript
that web browsers now support that will be powerful
as we begin to develop more sophisticated web pages.
And that is a feature known as web sockets.
And so normally when we think about making web page requests
on the internet between clients and servers,
we think of it in terms of the request response model,
where I, on my computer, make an HTTP request to some web server requesting
for some information, and then that server gives back to me some response.
And that's useful so long as any time I need data,
it's only because I'm requesting that data first
and then I get back that response.
But what you'll soon see is that in Project 2, which
we'll release later today, what you're going to be building is a chat
room-style application, kind of like Slack
that we've been using in the class.
And what that will mean is that it's not really
going to be the case that if someone sends a message via some chat room,
that you're not going to want to refresh your page in order
to request any new messages and then get those messages.
What you want is real-time communication,
where your client and server can both be talking to each other simultaneously,
or what's called full-duplex communication.
And to do that, modern web browsers support
what are called web sockets, which allow for a protocol that
allow for that full-duplex communication between client and server
simultaneously that allow for more real-time communication
between the client and server.
And Socket.IO happens to be one example of a JavaScript library
that lets us do things like this.
So we'll take a look at some examples of that
in order to show you how this can work and how it can ultimately be powerful.
And so what we're going to do is build an application
that allows different people on different pages
to cast votes for something.
They can vote yes on a motion or on a bill, or they can vote no,
and they can vote maybe-- is what we're going to try and do.
And ultimately, we're trying to build out
an application that lets anyone logging in see the results of this voting.
Lets anyone see whenever someone else votes in real time.
So let's take a look now at vote0application.pi.
And so what's going to happen here is I am going to define, inside
of my index function, the route.
I'm going to return the index.html page.
And what I'm going to use is a library called
Flask Socket.IO, which is going to allow me to use web sockets inside
of a Flask application.
And you'll see how all this works in just a moment.
But the idea of it is going to be that my web server and my web client
are going to be sending events, or emitting events, that are broadcasted
to everyone else; and they're going to be listening for events, receiving
those web socket events; and doing something
interesting with those events.
And let's take a look at just some of this code
and then get a sense for what's happening.
If we go to vote0index.html, what we see is inside of index.html,
it's pretty simple.
I'm including an index.js JavaScript file, which we'll soon
see at work in just a moment.
And inside the body of this application, I
have an unordered list that's going to keep track of all the different votes
that people have cast.
And then three buttons--
a Yes button, a No button, and a Maybe button.
And notice, in particular, that each one of these buttons
has a date-of-vote attribute, where date-of-vote is just
me giving additional information to these buttons
such that, in JavaScript, I can extract this data out of it.
And so I have a data vote that's equal to yes, one that's equal to no,
and one that's equal to maybe.
And so the interesting stuff is all going to happen inside of index.js.
And so what's happening here?
Well, when the web page is done loading, the first thing I need to do
is connect to the web socket to allow for this communication in real time
between the client and the server.
And so this is just a standard line that Socket.IO will use.
We'll connect to location.protocol, which will be either HTTP or HTTPS,
most likely, plus the domain name, and then whatever
port you're currently working on.
So you can sort of just copy this line and treat it
as, this is what's going to allow me to connect to this web socket
implementation.
And now these web sockets can listen for particular events.
And so in this case, what I want to do is once the web socket is connected,
I want to make sure all of my buttons are configured
to do something with the web socket.
In particular, when the button is clicked,
I want to emit a new event, telling the world,
or telling my web server in particular, that a vote has been submitted.
So how am I going to do that?
Well, once the web socket is connected, here is the code that I want to run.
I'm going to do a querySelectorAll, selecting all of my buttons.
And for each one of those buttons, here is the code that I want to run.
When that button is clicked, I'm going to run this function.
And that function is going to define a variable called
selection, which is equal to whatever the button's data-vote attribute is.
Remember, data-set gives me access to all the data
attributes of the HTML element.
And so each of my buttons had a different data-vote attribute--
either yes, or no, or maybe.
And I'm going to save that in a variable called selection.
And now I'm going to run this code, which is the interesting code.
socket.emit is going to emit this event to my web server
in order to notify my web server of this new event.
I'm giving the event a name.
I'm just going to call it submit vote.
And then I'm passing in the data that is associated with that event.
In this case, it's just a JSON object that contains a key called selection.
And its value is whatever it is that I actually
selected-- either yes, or no, or maybe.
So what this code will ultimately do is that once my web sockets are configured
and connected, when I click on a button-- either yes, or no, or maybe--
it's going to figure out which button did I click on, and save
that in a variable called selection.
And then it's going to emit an event to my web server.
That event is called submit vote.
And I'm passing in that selection data as part of it.
And so now before I go on to what's actually
happening later on in this JavaScript code,
let's go back to the web server code.
So to draw the distinction, remember, this JavaScript code
is code that's happening on the user's own machine--
locally on their computer.
And here in application.pi is the code that's running on our Flask server.
And so down here, we have this special syntax--
socket.io.onsubmitvote.
So this is my way of saying, when the socket detects this event,
an event called submit vote, and recalled it in the JavaScript--
when we emitted the event, we named the event submit vote.
So that's going to connect it to this particular function.
So when someone submits the submit vote event, here's what you should do.
First, take the data and get at the selection,
because that was the data that was passed in via this event.
And save it inside of a variable called selection.
And so now if we think about this in terms
of making sure all of our computers know about what vote has been cast,
if one computer casts a yes vote and says,
I've submitted a vote, submit vote event, tell the server that I've cast
a yes vote.
And I now get this notification, effectively,
that a yes vote has been cast.
The next step for me is going to be to tell-- to broadcast to everyone else--
that yes, a submit vote has just been cast, and that vote was a yes vote.
And so that's what this emit function is doing.
And you can import it from Socket.IO up above.
And I'm emitting a new event called announce vote.
And I can also pass in data to it.
And I'm announcing that the selection is the selection.
And this is going to announce it to any other sockets that are listening,
not just the original user.
And so I've saved the selection, I'm emitting
a new event called announce vote, its selection
is equal to whatever the selection was.
And broadcast equals true means I want to broadcast it to everyone,
including the person who originally cast the vote, because they should
theoretically know about it as well.
So I've emitted this announce vote.
And so now back on the client side, back inside the JavaScript,
to continue this story, what's going to happen
is that I'm going to tell the socket, when you receive an announce vote
signal, this is what you should now do.
Run this function that takes, as input, the data.
We're going to create a new list item, just like we did
before using document.createElement.
The inner HTML of that list item is going
to be Vote recorded followed by whatever the selection was,
whatever it is that they actually voted for.
And then I'm going to append this list item to #votes, that list of votes
that I've been accumulating.
So to tell the story very briefly one more time, when I first
connect the sockets, I set it up such that whenever a button is clicked,
I emit a submit vote event, telling the server that a vote has been submitted.
On my server now, when I receive this submit vote event,
I check what the selection is, and I broadcast
to everyone this announce vote event, telling people
that a vote has been made, and I want to announce it to everyone.
And in everyone's index.js file now, when
they receive this announce vote event, they're
going to run this function, which is going to add to my list of votes,
telling everyone what that vote is.
So if I now go into vote0 and do Flask run, I'm going to go to this URL.
And what I'm actually going to do is I'm going to open up this URL twice.
I'm going to open it up in two different windows.
So you can think of this as two different users that
are both using the system.
They both have a yes, no, and maybe vote.
And so when I click on this Yes button, what ultimately happens is--
oh, sorry, refresh these.
When I click on the Yes button, it says, "Vote recorded-- yes."
And so how did that appear on both of them?
Well, on one side, I clicked the Yes button.
That sends the announcement up to the server
that I've submitted the yes vote.
And then the other person, without needing to refresh their page or reload
anything, is notified, because the web server that announces, or emits,
and broadcasts to everyone that a new vote has been cast.
So I can cast Yes vote, No vote, Maybe vote.
And it updates for all of the users that are connected to this socket.
And likewise on this side, I can also cast votes,
and it will appear on the first side as well.
And so this is Socket.IO in action.
And as you begin to develop your chat application,
you can see how this would be useful-- that when someone sends a message
and presses Return, the other person can see that message immediately
without needing to wait a couple seconds, and refresh the page,
or do something else in order to see the results of that.
We'll take a look at one other example of this, of just Socket.IO in action,
in terms of how this casting of votes and how the emitting of events
on the client and server work.
An improvement to this vote application would likely
be that I don't really care about seeing this hierarchical list of votes.
What I really care about is seeing the vote totals.
I want to know how many yes votes are there, how many not votes are there,
how many maybe votes are there.
And in particular, one other drawback of the approach we have right here
is that if I ever close one of these windows and then open it again, well,
now all the votes are gone.
Like, on this side, I don't see any votes.
And if I say no on this side, we'll now know it appears over here.
But because, by default, my page loads with an empty list,
I didn't get all that history of all of the previous votes that
had been submitted.
So I need some way of storing that information such
that, when future people visit the website later,
they can see that information as well.
So let's take a look now at vote1.
And inside of application.pi for vote1, we're going to maintain one thing.
I have here a variable, just a global variable, inside of Flask's memory
that's going to just store information, store
information that any client connecting to my Flask server
is going to be able to take advantage of.
And this is just going to be a dictionary
that stores the current vote counts.
Currently, yes's have zero votes, no's have zero votes,
maybes have zero votes.
And when I go to the default route, just go to the index page,
I'm going to return index.html, passing in that votes variable,
because I want my index.html template to know what the initial vote counts are.
They start out at zero, but maybe by the time that I get to the website,
different people have made different votes,
and I want to know what those current vote tallies are.
So if I go to index.html now, what I see inside my body
is three divs, one that says Yes votes.
And then I have a span.
Recall that a span is just a segment on my HTML page
that I can give attributes, like a name, or an ID in this case.
And I'm giving this one an ID of yes, because later on, I
want to fill something in to that spot.
But we'll see that in just a moment.
And that is going to be set to whatever votes yes is,
the Yes field of that votes object that I passed in.
And likewise, I'm doing the same for displaying
the current number of no votes and the current number of maybe votes.
And then these buttons are the same as well.
So what's happening now inside of index.js?
Well, this is mostly the same.
When I submit a vote, this is identical.
When I click on a button, I want to figure out what vote am I voting for
and then emit this submit vote event, passing in that selection.
What changes is what happens on the server side.
So what do I want to happen when a vote is submitted?
Well, when a vote is submitted, I still want
to get the selection of data.selection.
But rather than just emit it right away, I want to store it in memory.
I want to make sure my application knows what the vote total is,
so that the next time that a user comes to the site,
they're informed of that latest vote total.
And so I want to update this Votes dictionary that's keeping track of yes,
and no, and maybe votes.
And to do that, I'm doing votes to access this variable.
In square brackets, selection, where selection is going to be one of yes,
or no, or maybe.
And then plus equals 1, just to say, increment that total.
Increment either yes, or no, or maybe to be
one larger than it was to indicate that I now have one more vote.
And then finally, down here, I'm going to emit a new event called vote totals
and pass in this votes variable here, which
is just this Python dictionary that contains all of those vote totals.
That's the data that I want to send to all my clients.
That's the data that I want to broadcast for everyone to know about such that
now, if we go back to the client side of things
on my computer, what should happen when I get this vote total event?
Well, I want to update my yes span, the thing that
has ID yes, set its inner HTML to be whatever data.yes is, and likewise
with no and maybe, such that when I get the new vote totals back,
I want to update my current counts of what all the votes are.
And so the result of this, if I go in to vote one and do Flask run--
and I'll open it twice for good measure.
I now see yes, no, and maybe votes, and three buttons at the bottom.
And when I click Yes, for example, on one of them,
the vote tally updates on both my computer
and presumably someone else's computer that is also connected by web sockets
to the same web server.
And clicking on any of these individual buttons will result in updating that.
And unlike last time, where if I closed the window and then reopened it,
it reset back to the original state, because I'm saving this information
inside of Flask's memory, in server-side memory in my Flask application,
when I go back to this site now, I see the same vote
totals, because it's able to take that information,
pass it into the index.html template, and now allow me to continue
voting just as I was able to before.
So that's Socket.IO in action.
Ultimately acts as a way to allow for this real-time communication
and will likely prove helpful to you in Project 2
as you begin to dive deeper into Python, and Flask, and JavaScript,
and sockets in particular in order to build these increasingly more and more
dynamic web applications that include client-side code that's
really running on the inside of the browser inside of the client's
computer.
And all of this is just a taste of what JavaScript is really capable of.
We've just seen a couple of the features of DOM manipulation,
and sockets, and other features.
But JavaScript can do a whole lot more, as we'll
begin to look into in future weeks.
But for now, that's it for JavaScript for today.
And best of luck with Project 2.