[MUSIC PLAYING]
BRIAN YU: Welcome back, everyone, to Web Programming with Python and JavaScript.
And last time, we took a look at JavaScript--
that language that ran inside of a user's web browser, client side,
and allowed us to do a number of things to make our web pages more interactive.
JavaScript enabled us to display alerts, to be
able to manipulate the DOM, the structure of the web page,
in order to add content or see what content was already there.
And it also let us respond to user events.
When a user clicked on a button or submitted a form or typed something
into an input field, we could have JavaScript functions
run that responded to those events in order
to make our web pages more interactive.
Today, we're going to continue that conversation, in particular taking
a look at user interface design, looking at some common paradigms in terms
of user interfaces and how we can leverage JavaScript
to be able to achieve those goals to create interactive user interfaces that
will be valuable when users are interacting with our applications.
So one of the more common paradigms, especially nowadays, in web programming
is the idea of single-page applications.
Thus far, if we've wanted to create a web application that
has multiple different pages, we've generally
done that via multiple different routes in our Django web application,
for example, where you go to slash something to get one page
and slash something else in order to get another page.
But commonly, using JavaScript, we have the ability
to create single-page applications where the entire web page is really
just a single page, and then we use JavaScript to manipulate the DOM,
to replace portions of the page with things we want to replace.
And this has a number of advantages, one of them being that we only
need to make modifications to the part of the page that is actually changing.
If, for example, you have five different pages,
but the general layout and structure of the page
is pretty similar, when you switch between pages
rather than load an entirely new page, you
can just load the part of the page that is changing.
And this is especially helpful for applications
that are changing quite frequently.
So let's take a look now at how we could implement, for example,
a very simple single-page application.
So let's imagine, for example, that we want a single-page application that
just displays three different pages, but all included in the same page.
I'll go ahead and create a new file that I'll call singlepage.html,
inside of which we'll include our usual HTML tags.
And inside the body of this page now, I'm
going to include three different sections of the page
to represent the three different pages I might want to display to the user.
So I'll have a div whose ID is page1 that maybe just has
a heading that says "This is page 1."
And you could imagine there's more content on these pages as well.
A div whose ID is page2.
We'll say, "This is page 2."
And then one final div whose ID is page3.
It has a heading that says "This is page 3," for example.
Now, right now if I were to open up singlepage.html, what we'd see
is we see all three pages at the same time.
I know that's probably not what we want.
What we really want is by default to hide these pages
until we want to view the pages one at a time, for example.
So one thing I could do is use CSS to be able to toggle whether or not
something is visible, adding some style tags
to my page to say that by default, all of my divs
should have a display property set to none, meaning they're not visible,
they're not displayed on the screen.
Now, if I refresh the page, I don't actually see any of the three headings
that I had there before.
But what I'd really like is for some buttons
now to allow me to toggle between these three pages.
So I'll give myself three buttons--
one button that says Page 1, one button that says Page 2,
and one button that says Page 3 for example.
And I need some mechanism for these buttons
to know when you click on this button, what page should be displayed.
So I'll go ahead and use data attributes, which
we saw last time with JavaScript, to add some additional information
to these particular HTML elements, where I'll
give the first button a data-page value of page1,
the second one a data-page value of page2,
and the third one a data-page value of page3.
Here, again, just providing information so that later,
when I write some JavaScript, I can have the JavaScript code look
at the data-page attribute to say that when you click on this button,
you should let me see the div whose ID is page1.
That's what this is going to allow us to signal.
So now let's go ahead and write that JavaScript.
What I want to be able to do is to be able to say,
I would like to show page 1 and hide the other two, or show page 2
and hide the other two, or show page 3, for example.
And so to do that, I'll first write a function that will let me do that.
I'll write a function called showPage that
takes as its argument what page I want to show.
And so what should this function do?
Well, what we're going to do is we're going to say document.querySelector.
And I want to get the thing that has a particular ID the ID of whatever
this input happens to be.
This page is going to represent the ID of the div that I want to show.
So I'll say, get me the thing that has this ID.
And then using a template literal, I'll say, all right,
get me the ID of page, whatever element has that particular ID.
And then I'd like to change its style property.
Which part of the style?
Well, I want to change its display property.
And instead of none, which was the default here,
where I said don't show it at all, the other option for a div
is block, meaning it shows up as just a block that is
on the page that is actually visible.
And so now I have this showPage function.
And I can test it, in fact.
If I go into my browser, refresh the page, I now see three buttons.
The buttons don't do anything just yet.
But what I can do is in the console, if I actually just try running this,
I can run the showPage function and say, like, showPage("page1"), for example,
press Return, and all right.
Page 1 now appears on my page.
And if I [INAUDIBLE] showPage("page2"), then page 2 will become visible.
And all right, that did half of what I wanted.
Page 2 is now visible, but so is page 1.
So I probably want it such that if I ever show a page,
I hide the other pages first.
Like, hide all the pages and then show page 2, or hide all the pages
and then show page 3.
So how could I go about doing that?
Well, first, I might want to just, when I show a page, first
hide all of the other pages.
Hide all the pages.
So to get all the pages, I'll do document.querySelectorAll.
Get all of the divs, which is what I'm using to enclose the pages.
And now for each one of those-- again, effectively creating a loop where
I'm looping over each of the divs--
for each div, let's go ahead and set the div.style.display display
property equal to none.
And so what this showPage function is now doing
is it is first querying for all of the divs, which
are simulating my pages inside of this single-page application.
And for each one of the divs, we're going
to pass it as input into this function, which is the argument to forEach,
again using this arrow function notation, which is just a shorthand
way of expressing a function.
Where I'm here saying that for each of the div,
we'll go ahead and modify its style property,
setting display equal to none, meaning don't show any of the divs,
and then show only the div that was requested.
So now this should solve the problem of multiple pages appearing
simultaneously.
But if I go back to this page and I click or write showPage("page1"),
then page 1 appears.
But if I run showPage of page 2, then page 2 appears but page 1 disappears.
And likewise, when I ShowPage("page3"), that shows page 3,
but not the other two.
So I can manipulate which page is visible all via the console,
but now what I'd like to do is get these buttons to actually work,
where if I click on one of the buttons, that has the effect of actually
displaying the requested page.
So in order to do that, well, I want to attach some event listeners
to these buttons, which means I need to wait until those buttons have loaded
onto the page.
So we'll use document.addEventLis tener('DOMContentLoaded'), again,
waiting until all of the content on the page has been loaded.
And then and only then will I say, let's go ahead
and querySelectorAll for all of the buttons.
And for each one of those buttons, let's go ahead and attach an event listener
to each of those buttons.
So I'm querying for all of the buttons and saying, for each of the buttons,
I would like to do this with each button.
And what I'd like to do is say button.onClick.
When the button is clicked on, go ahead and run this function.
I'd like to showPage, and which page do I want to show?
Well, I want to show whatever page is in the page part of the buttons data set.
And to get it the current button, the button that has been clicked on,
recall that when we're inside of an event handler,
we can take advantage of the JavaScript keyword
this, which refers to whatever element has received the event,
so whatever button, in this case, that was clicked on.
So I can say this.dataset.page to mean that all right,
for each of the buttons, when the button is clicked on-- we're saying
button.onClick for each of the buttons--
run this function when the button is clicked.
We'd like to show a page.
Which page do we want to show?
We'll take this button, the button that received the event,
access its data properties, access its data page attribute,
which are down here-- either page1 or page2 or page3--
and go ahead and just call the showPage function that we wrote a moment ago.
So now that we've done that, we've attached these event handlers
to the buttons.
So now if I refresh the page, I can click on these buttons
and toggle between any of the three pages.
And now the interesting thing here is that we now
have the ability to just, in a single page,
allow myself to simulate the idea of having multiple pages, all enclosed
in a single HTML file but not needing to consistently make
additional requests to a server in order to get access to that information.
Now, sometimes, though, it might be reasonable to want
to reach out to a server--
when you need new information for a page, for example.
You might imagine that each of these pages contains a lot of text.
It's going to be inefficient if immediately, we're
loading all of that data into HTML and just showing and hiding them
when we need to, because maybe we're loading
more information than the user is ever going to actually care about,
if they're never going to look at page 2 or page 3.
So one thing we might imagine doing is loading this data dynamically.
Last time when we were talking about JavaScript,
we saw how we could use fetch to say go ahead
and request some additional information from a web server-- last time,
it was currency exchange rates.
But then we used that data that came back in order
to fill in something onto our page.
And likewise, we could do a similar thing here--
that if we have the general structure of a single page
and we want to load new content, rather than load entirely
new HTML content and reload the entire page,
we can just ask our own web server for what part of the page
needs to change and then just replace that part of the page.
And so that's what we'll take a look at now,
now combining Django for our server and JavaScript
for writing the client-side code to be able to generate
a single-page application.
And so for this, we'll go ahead and go into an example
I had in advance called singlepage1.
And inside of singlepage1, this is just a Django application
with a single app called singlepage.
And what we'll notice is we'll go to the URLs first.
There are two URLs--
one default URL that just loads the index function, and then a URL
for loading different sections of a page that I might want to dynamically load,
for example.
So I have sections/ some particular number.
And if we look at the views for what it is these URLs are actually doing,
the index function just returns index.html.
And then what the section function does is
it first makes sure the number is between 1 and 3,
and if so, responds with one of these just strings of text, for example.
So how does this actually work?
If I go into singlepage1 and run the server,
if I go to this URL /sections/1, for example,
what I get is this block of text.
And if I go to /sections/2, I get that block of text.
/sections/3, a different block of text altogether.
So just different text, and I'd like to incorporate this text into an existing
HTML page, for instance.
So here, I'll go into index.html, this template that gets
loaded when I go to the default route.
And inside of index.html, what we'll see is
I have a showSection function that behaves
very similar to the showPage function we saw from a moment ago,
but instead, what showSection is going to do
is it's going to fetch what text I should display
on the page from my own web server.
I'm fetching from /sections/ fill in a number here, number 1 or 2 or 3.
When I get the response, in the past we've
seen how we can convert that response into JSON data,
if it's unstructured data.
We can also just convert the response into plain text.
Then I'll take that text, console.log it just
so we can see it in the log output.
But then go ahead and query select for the content of the page, something
that has an ID of content, update its inner HTML,
and set it equal to that text.
So what this entire function is now doing
is it is going to reach out to my server,
figure out what text content belongs in the new section,
and fill in the part of my page accordingly with the text that
comes back from that HTTP request.
And then down further below, inside of the page,
we'll see that I have a Hello heading, three buttons that
toggle between the different sections.
Each of them has a data-section attribute this time
for which section should be loaded.
And then a div that is initially blank just for the content of the page.
So putting this all together now, if I go to the default route,
I see "Hello!" plus three buttons to give me
a choice between three different sections.
And if I click Section 1, what's going to happen is JavaScript is going
to query /sections/1, ask for the text.
It gets that text back, and it's going to fill it in into the page--
Section 1, Section 2, and Section 3.
So very similar to before, but unlike what we had before,
where all of the text was being loaded into the HTML page all at once,
now we're using asynchronous JavaScript to only dynamically load
information when we need it.
When we click on a section, then it's going
to make the request for what content needs to be filled in,
and it's going to fill it in.
And everything else-- these buttons, this
heading, and you might imagine in a more complex website,
you've got a lot more going on around the edges of this web page--
and all of that stays the same.
We don't need to reload any of that information.
We're only reloading the portion of the page that
actually changes as we toggle between these various different section
headings.
Now, this seems to be an advantage in some ways,
that maybe we can be more efficient about how we run
our single-page applications like this.
One thing we seem to lose, though, is the notion
of maintaining state inside of the URL.
That generally, the URL gives you an indication for what page you're on.
You're on something like /1 if you're on section 1 or /2 if you're on section 2,
/3 for section 3.
But of course, we're staying on the same page in all of these examples.
Whenever I click a button--
Section 1 or 2 or 3--
the URL is never changing.
The URL stays the same.
It turns out there's a way in JavaScript to manipulate that URL,
taking advantage of what's known as the JavaScript history API, where
I can push something to the history, meaning update the URL
and actually save that inside the user's browser history so later on,
they could potentially go back to that.
And to do that, I'll show you yet another example inside of singlepage2,
which is very similar, except inside of index.html,
I've added a couple of additional things.
One is that when I click on a button, meaning
when I click on Section 1 or Section 2 or Section 3,
I've added this line here, history.pushState.
What history.pushState is going to do is it
is going to basically add a new element to my browsing history, where I first
specify any data associated with the state.
So in particular, I'm storing a JavaScript object
representing what section number is being represented here.
Next is a title parameter that most web browsers actually ignore,
so that can generally be the empty string.
But the third argument here is what should go in the URL.
And what I want to go in the URL, in this case,
is something like section followed by the section number.
So I can go to /section1 or /section2 or /section3, for instance,
and those will appear in the URL bar when I click on a different page.
Then what I want to be able to support is the ability
to say when I go back through my history,
if I click the Back button in my web browser,
I'd like to go back from section 3 to section 2,
if that was the page I visited previously.
And there turns out to be an event handler for that as well--
window.onpopstate, meaning when I pop something off of the history,
like go back in my history, we have the ability
to take some event as an argument.
And if you look at event.state.section, which I've run console.log on,
so we can take a look at it in a moment, we'll
see what state was stored associated with that part of the user's history,
and I can go ahead and show that section.
So all in all, when I run this web application,
going into singlepage2 this time, when I run the server, I see "Hello!",
three sections for buttons.
When I click on one of those buttons, not only do I see text,
but I also see in the URL bar that I'm now on /section1.
That has been pushed onto my history, and I've
updated the URL to reflect that, too.
I click Section 2.
That updates the URL as well.
Section 3 updates the URL, too.
And when I've pushed things onto my history,
I've associated some state with them so that I can go back if I ever need to.
And in fact, if I open up the JavaScript console now and I go back, for example,
back to section 2, what you'll see is that what gets logged is the number 2.
When I print out what is the current section that's
associated with this URL, it's saving that state,
that I should be loading section number 2.
And so it does load section number 2 here.
So there's certainly nothing wrong with the original paradigm
of just loading different pages dynamically using Django,
like make a request and get a response.
But oftentimes, as you begin to imagine applications where a lot of things are
changing on the same page simultaneously--
you might imagine social networking websites where a lot of things stay
the same, but new posts might be added and you might be looking at different
parts of the same page--
being able to dynamically load information,
request additional information, and then display it on the page
can actually be quite powerful and a way to make your web pages a little bit
more interactive.
So that then is how we might build single-page applications-- taking
advantage of JavaScript to asynchronously load new data,
and then taking advantage of this history API
that let us add things to the URL, add things to the user's browsing history,
such that we could go back to them later by listening for window.onpopstate.
And it turns out that window object that we get access
to in JavaScript is quite powerful.
It represents the physical window on the computer screen
that displays all of their web content.
And there are certain properties of that window
we can look at that allow us to enable some interesting features.
So for example, your window is really described
by what the user actually sees inside of their window in Google Chrome or Safari
or whatever web browser they happen to be using.
And there are a couple of properties that might be of use.
Something like window.innerWidth will represent
how wide is the window, which might be useful to know
to know the size of the user's screen, for example, to know how many pixels
wide the window happens to be.
And just as there's a window.innerWidth, there's
also a window.innerHeight that represents the height of the window
as well.
Now, window represents the physical part that they're actually seeing.
We've also seen another variable that JavaScript gives us access to,
and that is this document object.
So what is the difference between the window and the document?
Well, the document generally represents the entire web page.
But if web pages are long, oftentimes the web page
doesn't fit entirely inside of the window--
that you generally have to scroll through an entire web page
and the window is only showing you one portion of that page at any given time.
So you can represent the document as, like, this big vertical section
that goes beyond the window.
There might be part of the document that is
above the window, part of the document that is below the window as well.
So window.scrollY is another variable you have access to on the window,
and window.scrollY represents how many pixels far down have you scrolled.
So if you're at the top of the page, window.scrollY
is 0-- you haven't scrolled at all.
But as you begin to scroll, if you want to know how far the user has scrolled
on a page, you can look at window.scrollY
to figure out the number of pixels the user has scrolled in the Y
direction, the up and down direction.
And the entire height of the page is represented
in document.body.offsetHeight.
That represents how tall the entire height of the document is.
And we talk about all this in addition to things like window.innerHeight
and window.innerWidth because using all of these values together,
you can begin to do some interesting calculations.
So one thing you might want to detect, for example,
is has the user scrolled down to the bottom of the page or not?
That might be something you care about knowing.
And it turns out, there isn't an event listener that does this automatically.
But we can calculate it in order to try and figure this out.
If innerHeight is the height of the window,
scrollY is how far vertically the user has scrolled,
and document.body.offsetHeight is the entire height of the document, you
can ask yourself, what needs to be true if the user has
scrolled to the bottom of the page?
And well, if the user has scrolled to the bottom of the page,
well, then scrollY plus the innerHeight, meaning
the amount that they've scrolled, plus the height of the window, that
must be at least or equal to document.body.offsetHeight, meaning
the amount that they've scrolled plus the window takes you down
to the bottom of the page, to the end of a page, to
however tall the document happens to be.
And using that mathematical comparison, we
can actually detect when the user has reached the bottom of the page
and we can actually try and now put that into practice.
So I'll go ahead and open up an example that I have here called scroll.html,
and all scroll.html has right now is 100 paragraphs.
Inside of the body tag I have a p for paragraph, paragraph 1, paragraph 2,
so on and so forth.
I have 100 paragraphs inside of the body of this HTML page.
And that's all that really is there right now, such
that now if I go ahead and open scroll.html,
I see that I have 100 paragraphs that I can scroll through.
And what I might like to do is detect when
I've reached the bottom of the page and maybe do something when I do so,
something like change the color of the page, for instance.
So how might I go about doing that?
Well, I'm going to need some JavaScript.
So I'm going to add some JavaScript, and I'll add an event listener
for window.onscroll.
Onscroll is an event that listens for when I'm scrolling through the window.
And when I scroll through the window--
we'll go ahead and run this function.
We'll just use an arrow function as a shorthand here--
what do I want to calculate?
Well, I want to calculate if window.innerHeight,
meaning the height of the window itself, plus window.scrollY,
meaning the amount that I've scrolled, if that
is at least document.body.offsetHeight, well,
that means I must have scrolled to the bottom of the page
or maybe even a little bit further, if there's a little wiggle room
to scroll past the end of the page.
So if this is true, well, then I've reached the end of the page.
And then we'll go ahead and say document.querySelector('body').
And let's go ahead and change its style, in particular change
its background color, and change the background color to green.
Otherwise, if we haven't reached the end of the page,
then we'll take the body of the page and change its background color to white.
So what we're now doing here is taking advantage
of the properties we know of this window object,
saying when we scroll the window, let's check to see if we add this up
and at least the height of the entire document,
we've reached the end of the page.
Go ahead and change the style of the background of the body accordingly.
Otherwise, change the background to white
or leave it at white if it already is.
So now if I take a look at this actual HTML page and reload scroll.html,
we'll see that the background is initially white.
But as I scroll down, once I reach the bottom,
we'll see that the page changes to green.
It's white before I reach the bottom, but as soon
as I get to the bottom of the page, it turns to green.
And the reason why is because the height of the window, height
of the window here, plus however much I've
already scrolled from the top of the page up until now,
that together is equal to the entire height of the document, which
means we're able to detect the fact that I reached the end of the page.
And as a result, we can change the color of the background to green.
Now, this in itself is not a particularly practical use
of detecting when we've scrolled to the end of something.
We probably don't usually care about changing the background color
when you reach the end of the page.
But there actually are real applications,
and you might imagine this in the context of websites that
allow for things like infinite scroll--
that if you're on a social networking website that
has a whole bunch of posts, you scroll to the bottom of the list of posts,
and then it generates the new set of posts as well.
Or you're looking at news articles and you're scrolling through news articles,
and once you reach the bottom, it'll load a whole new set of news articles
without you having to go to another page.
How is it doing that?
Well, it's a combination of the same types of ideas
that we've been talking about.
Number 1, the ability to detect when you've reached the end of the page
using JavaScript to detect that you're at the bottom of the page,
and number 2, to be able to asynchronously load, using JavaScript,
additional content-- fetch some additional page that has some
additional content, some additional news articles, some additional posts,
and whatnot--
and then take that information and manipulate the DOM
to add that information to the existing web page.
And that, ultimately, is what's going to give us
this power to be able to support something like infinite scroll.
So let's now go ahead and try and see what it would
look like to implement infinite scroll.
I've already started to create a sample application
inside of this application called scroll,
and I've got an app called posts inside of it.
And what the posts app does is it's got a couple of URLs.
It's got a default URL that just loads an index route, and then a posts route
that loads this posts view.
And so lets look at what these do.
Index, all it does is it's going to load a file
called index.html, this template.
And if I make a request to /posts, I need to provide two arguments.
I need to provide a start for what post I want to start with,
an end for what post I want to end with, and then
it's just going to generate some sample posts that
just say, like, Post number 1, Post number 2, so on and so forth.
In practice, you can actually use social network posts in place of this,
but this is good just for demonstration purposes.
So what this is going to do, if I go into scroll and runserver,
is that if I go to /posts and say start=1 and end=10, for example,
then I get a JavaScript object that looks like this.
Recall that a JavaScript object is just a convenient format
for passing information back and forth in JSON format.
And what we have here is a JSON object with a key
called posts that gives me all of the posts--
Post number 1, Post number 2, all the way up to number 10.
And it's giving me those posts because I said start at 1, end at 10.
But I could have specified other numbers as well.
If I had said something like start at 20 and go to 28,
then it's going to give me post number 20 through post number 28.
I can specify the range of posts that I want.
So this now is an API that I have implemented, effectively,
that allows someone to get access to a variety of different posts
by hitting this particular URL, this endpoint, so to speak,
and passing in parameters-- passing in what post they want to start with
and what post they want to end with.
And then they get all of this data back presented to them
in JSON format that can then be used.
And what's nice about this is that now when we're loading posts,
rather than have to just guess at how many posts we need to load
and then require someone to go to another page,
we can just do something like load the first 20 posts,
and now what we'd like to do is if they reach the end of the page,
go ahead and load the next 20 posts by hitting this API endpoint,
getting the next 20 posts, and then filling that in into the HTML page.
So let's see now how that actually works in practice by taking
a look at that template in index.html.
So go into templates, index.html.
And there's a fair bit of JavaScript here, but look at the body first.
The body just has a div for all the posts
that initially is going to be empty.
Now, here's what the JavaScript is going to do, and we'll walk through it.
We start with the first post, so counter is
going to keep track of what post we need to load next.
By default, we're just going to start by loading post number 1.
We have a variable called quantity that's
going to tell us how many posts are we going to load at a time.
Let's just say load 20 posts at a time.
So start with 1 to 20, then 21 to 40, 41 to 60, so on and so forth.
And when DOM content has loaded, go ahead and just
call this function that's called load.
And what the load function does is it figures out
what the start and end should be, it fetches all the new posts, and then
for each of the posts that comes back, is it figures out what [AUDIO OUT]..
So we're asynchronously asking for new posts.
And what the add_post function does is it creates a new div,
populates the post inside of it, and adds it to the DOM.
So now that we have these parts, the ability
to load new posts as by fetching from some URL
all of the posts that we care about, and then for each of those posts
that comes back, add something new to the DOM
as by creating a new HTML element and inserting it into the page,
we have the ability to dynamically load all of these posts.
So if I go not to /posts, but just to this default route,
I'll see that we have something like 20 posts that all show up.
But just 20 posts.
Because every time I call the load function,
that is going to load the next set of posts, for example.
And so what I can do is in the console, if I try running the load function just
by calling it myself, press Return, after a second
or so, the next set of posts show up--
21 all the way through 40.
I call load again.
The next set of posts show up--
41 through 60.
20 posts at a time, all using that asynchronous JavaScript.
But now what I'd like to happen is for all of this to happen on its own,
without me having to intervene and manually write JavaScript calls.
I would just like to say, well, the same type of logic as before--
window.onscroll.
Lets go ahead and say if window.innerHeight plus window.scrollY
is at least document.body.offsetHeight, meaning
if I have scrolled to the end of the page, well,
then just go ahead and call the load function.
That's all these lines are doing.
Every time I scroll, we check.
Did we scroll to the end of the page?
And if we did scroll to the end of the page,
then go ahead and load the next set of posts.
So now I refresh the page, I see Post #1 all the way up through Post #20.
Now watch what happens when I get to Post #20.
If I scroll to the bottom, after a second, the next set of posts appears.
I scroll to the bottom again, I'm at 40.
And then after a second, the next set appears.
Every time I scroll to the bottom, more posts are going to load after that,
allowing me to effectively implement this idea of infinite scrolling
by taking advantage of some JavaScript techniques, where
I can check for when I've got to the end of the page
and then dynamically do something as a result of that, something like load
some additional pages onto the screen.
And so here, too, a lot of power to be had inside of JavaScript.
And a lot of where the power of user interface comes from
is from how it is that the user interface interacts with the user,
thinking about what the user is going to do
and how the page should interact as a result-- something like user scrolls
to the end of the page and they see some new pages show up as well.
And one technique we can use for just making HTML elements a little more
responsive, a little bit more interesting,
is by adding some animation to them as well-- the ability for things
to move around and change their properties in some way.
And it turns out that CSS has support for animation.
CSS has already given us support for things like styling elements,
saying we want this element to be of this color and this size, for example.
But it also gives us the ability to animate those properties as well,
to change the size of something or change the position of something
over some amount of time.
And so let's now take a look at an example of what that might actually
look like.
I'll go ahead and create a new file and I'll call it animate.html.
And inside of animate.html, I'll go ahead
and start by including our usual HTML.
Title is Animate.
And what I'd like to do is just add a little bit of animation using CSS
into this particular page.
I'm going to start with just a heading, a heading that says something
like "Welcome!", for example.
It's just going to display a welcome message.
Such that now if I open animate.html, here's what I see--
just a message that says "Welcome!"
But now let's add some CSS to it.
Let's go into the style tag.
And for h1, for this heading, I'd like to apply a particular animation to it.
And I first need to specify what the animation's name is going to be.
And I can pick a name for the animation.
I'll say something like "grow," for example.
I'll set the animation's duration to be 2 seconds.
And then the animation fill-mode is like what
direction should the animation move in.
Should it go forwards?
Should it go backwards?
We'll generally want our animation to go forward,
so they're making some sort of forward progress according to some rules
that we're going to specify.
So here I'm saying we're going to animate all of our headings
using an animation called grow, and now I
need to define what that animation actually does.
And to do that, up above in style I'm going to say @keyframes grow.
And what this is going to allow me to do is
specify some keyframes for this particular element,
meaning where should the element start, what should its style properties
be, and then at the end, what should its style properties be?
And CSS is going to take care of the process of figuring out
what needs to happen in all those intermediary fractions of seconds,
for example.
So what I can say is something like, go ahead and grow from--
meaning what should its initial properties be,
and maybe initially I want it to have a font size of 20 pixels--
and then we'll say to font size of 100 pixels, for example.
So all in all, what this is saying is I would
like to apply an animation called grow to all of my headings.
This animation should last 2 seconds and go forwards.
And what is the grow animation going to do?
Well, it's going to mean at the start, anything
that obeys the grow animation will start with a font size of 20 pixels,
and at the end, it will grow to a font size of 100 pixels.
And I have now defined what it is that that animation means.
So now if I go ahead and refresh this page,
animate.html, you'll see that "Welcome!" changes size.
Over the course of 2 seconds, it goes from smaller to larger by obeying those
keyframes.
I told it to obey this particular set of instructions
where it goes from a particular font size to another font size,
and as a result, we see the effect here on the page.
And it turns out you can do more than just manipulate size.
You can manipulate just about any CSS property you want.
So if I tell the heading that it should have a relative position,
meaning its position should be relative to other elements or other things
than its parent, I can say you should change your position from being 0%
away from the left side of the screen to being 50% of the way
from the left side of the screen.
And at this point, "grow" is probably not the best name for this animation.
I'll call it "move" instead.
So animation name is move.
And so now what this animation is going to do
is it's going to say when you run the animation,
go from being right next to the left side of the screen to being about 50
away from the left side of the screen.
So I can go ahead and rerun this.
And we see that's the animation that takes place.
It goes from the left all the way back up to about halfway across the screen.
Refresh the page and it goes ahead and does the exact same thing.
And it turns out, we don't just need to specify a beginning point and an end
point for an animation.
We can specify various different keyframes for different points
within the animation that we would like to capture,
something like at the beginning of the animation,
have this set of CSS properties, maybe halfway through the animation,
have a different set of CSS properties, and then at the very end,
have yet another set of CSS properties.
So I could say something like, if I want the heading not just to move
from left to right but also to move back again, I can say at the beginning,
at the 0% point when you're 0% of the way through the animation,
you should be 0% away from the left-hand side.
When you're 50% of the way through the animation,
you should be 50% away from the left-hand side.
And then when you're done with the animation, 100% of the way through,
let's go back to 0% away from the left-hand side.
I now have three keyframes-- beginning of the animation,
middle of the animation, back to the beginning of the animation again--
and the effect of this is if I refresh the page, we go to the right
and then we go back.
We're able to move one direction and then move back.
And there are other properties we can use
to manipulate these animations as well.
I can set the animation-iteration-count, for example,
to 2, to mean rather than just do the animation once and then stop,
do the animation twice and then stop.
So I refresh, it goes to the right and then it goes left,
and then it repeats that a second time.
And it turns out if you really want, you can
set this to infinite, to mean never stop performing that animation.
It's consistently going to have this heading, move to the right,
and then move left, according to those keyframes that I have specified.
And so if you ever see things moving around on a page,
interactive in some way, there are a number of ways to do it.
You can animate things using JavaScript, for example.
But there are many cases where CSS alone is pretty good at just
creating these types of animations.
And while this animation right now is just running forever,
we could use JavaScript in order to control that animation as well.
So let's see an example of what that would look like.
I'll go back here.
In the body of the page, in addition to a heading
that says "Welcome!", I'll go ahead and add a button that
just says "Click Here!", for example.
And now what I'll do is add a little bit of JavaScript.
I'm going to add some JavaScript so that the button can now
control the animation, decide when the animation is going to start and stop.
And so what we'll do inside of the script is to first say
document.addEventLis tener('DOMContentLoaded'), meaning wait
until the DOM is done loading, as we've done before.
And let me now get that h1 element--
document.querySelector('h1').
And initially, I'm going to set its style.animationPlayState
equal to paused.
So animationPlayState is a property of the style that
lets me decide if the animation is playing or paused,
and I can control that using JavaScript.
Rather than just say run infinitely forever,
I can say the animationPlayState should start out as paused, by first getting
the h1 element and then modifying the animationPlayState property
of that particular element.
But now what I'd like to happen is any time someone clicks on the button,
I want to change the animation play state.
So I'm going to say document.querySelector('button'),
meaning get that button, and when someone clicks on the button,
let's run this function where if the current animationPlayState is paused,
well, then, go ahead and set animationPlayState equal to running.
And otherwise, if it's already running, then
let's go ahead and set the animationPlayState equal to paused.
So all in all, what this function is going to do
is it's going to get me the heading, pause this initially,
and every time the button is clicked, run this function where
the function says if we're paused, go ahead and start running the animation.
Otherwise, go ahead and pause the animation
by modifying that animationPlayState property of the heading.
So now if I refresh this page, right now we have "Welcome!" plus a button
that says "Click Here!"
And initially, everything is paused.
There's no animation happening.
But I click here and that begins the animation, which
would go on indefinitely until I decide that I want to stop it, at which point
I click it again and the animation pauses.
And I can control when to start and when to pause that animation as well.
And so this can be helpful and nice when you
want to create something a little bit more interactive,
something animated on the page.
But this is especially helpful because it means that you can gradually
change CSS properties over time.
Rather than just immediately change something,
you have the ability to animate something
to make it work a little bit better.
So let's take a look at an example of how
you might put that idea into practice.
Let's go back to our posts example, where we had this infinite scrolling
list of posts, but imagine now that we want the ability to hide posts
when we're done with them.
So I've prepared an example called hide, which
is very similar to what we had before.
But this time, I've just added one extra button and the button
says "Hide" on every single div.
Right now, clicking the Hide button does nothing.
We'll go ahead and implement that in just a moment.
But first to see how this worked, if you go into hide,
go into the index.html template.
The only change that's been made here is what happens when I add a new post.
Recall again that what this application does is it loads posts from a server.
And then when it gets to those posts, it loops
over each of the individual posts, which is just a string of text,
and it adds that string of text inside of an element
onto the page via this add_post function.
And what the add_post function is going to do here
is first create a new element, create a div in which to store that post,
give it a class name, because that's how we're going to animate it,
and then set its inner HTML equal to the contents of the post--
something like post number 1, post number 2, post number 3--
and then add a button that just says "Hide."
And then we're going to go ahead and add that to the DOM as well.
So that's what add_post is now going to do.
We're sort of generating some HTML using this JavaScript code
and then adding that HTML to the page.
And now we're adding is a div that has not only
the contents in the post as text, but is also
going to give us access to a button that ultimately we
hope is going to let us hide that post as well.
So how do we actually get the hiding of the post to work?
Well, what we want to do is somehow detect
when the user clicks on one of those Hide buttons.
So there's a number of ways we could do this,
but one way is just to listen for any time anyone clicks
on the document as a whole.
Any time anyone clicks on the document, I
might like to ask something like, what did they actually click on?
And it turns out that with most event listeners,
the function the event listener takes in can
take as an optional argument the event itself, which
is a JavaScript object that contains information about the event that
happened, like the click event or the scroll event
or the keydown event or the keyup event, for example.
And one of the properties you get access to
is event.target, which is like what was the target of the event?
In this case, what was the thing that was actually clicked on?
And I'll go ahead and save event.target inside of a variable called
element, where the idea now is that whatever gets clicked on, that is
the event's target.
We're going to save that inside of element.
And what I want to know is is element, is that one of the Hide buttons?
I want to know, is it a Hide button.
I could have also attached an event listener to each of the Hide buttons.
This is just an alternative way of doing it
that I'm showing you for sake of demonstration
where we say when we click anywhere in the document,
figure out what was clicked on and save it inside of this variable.
And if it's a Hide button, then it's going to have a class of hide,
because I gave every Hide button a class of hide.
And so what I can say is if element.className equals hide, well,
that means that what was clicked on is something with the class of hide,
we can assume that it is in fact a Hide button.
And then what I want to do is I can do something like element.remove
to say go ahead and get rid of that element.
So now what does this do?
If I refresh the page--
let's try it.
Post #1.
If I hide it, I want to hide Post #1.
All right, that didn't quite work.
It was close-- it got rid of the Hide button.
But I didn't want to get rid of the Hide button,
I wanted to get rid of the whole post.
So what's going on here is it seems to be that if the element's class name is
hide, meaning I clicked on a Hide button,
element.remove just removes that element.
It removes the Hide button, but it doesn't
remove the post that contains it.
And if you think about this in terms of the DOM, the post is a div
and its child element is the button, this Hide button.
And so you remove the button, but it doesn't also remove the post as well.
If you want to remove the post as well, you
need to remove not the element, but the element's parent.
And in JavaScript, it turns out there's a way to do that, too.
Rather than element.remove, I can say element.parentElement.remove to say
take the element, get its parent, and remove that.
So now I refresh the page.
Now I see a Post #1.
I want to hide it.
I hide Post #1 and all right, now I see Post #2 and Post #1 has gone away.
If I want to hide Post #3, I hide Post #3.
Now Post #3 is gone.
Now I go straight from Post #2 Post #4.
So this works, but it's also not immediately obvious what's going on.
Because all of the posts are the exact same height, when I get rid
of Posts 1 and 3, it's not immediately obvious to the eye
that they've gone away because Posts 2 and 4,
they look almost exactly the same.
You really have to be paying attention to know that the hiding worked.
And so this can be a time where animation can actually
be quite helpful.
So what I can do is say something like, let's go ahead
and give this post an animation associated with every post.
We'll give it an animation-name called hide,
an animation-duration of 2 seconds-- we'll say it'll take you 2 seconds
in order to hide--
and an animation-fill-mode of forwards, I
want to go forwards with the animation.
And initially, I'll give the post an animation-play-state of paused, meaning
initially, I don't want the animation to be running, because I don't
want to hide all the posts immediately.
Pause this animation.
Later, we'll go ahead and run the animation
in order to actually hide the post.
Then I need to define, what does it actually mean to hide the post?
And I'll say, well, all right, at the 0% mark, what does it mean?
Let's give yourselves an opacity of 1.
Opacity is a CSS property that just controls how opaque
or how transparent an HTML element happens to be.
And at the end, 100% of the way done with the animation,
we'll set opacity to 0.
So initially, we can fully see the element.
At the end, the element is totally transparent.
And now what I need to do is actually trigger this to happen somehow.
So this is probably going to happen inside of my event listener.
We're-- instead of removing the element right away,
let me just take the parentElement and set its animationPlayState equal
to running, for example, meaning when I click the Hide button,
go ahead and run the animation that will change the opacity from 1 to 0 over
the course of a couple of seconds.
And then if I really want to, I can add another event listener
to say take the parentElement, addEventListener.
There's an event called animationend, which
happens when the animation is over.
And then I can say, all right, when the animation is over,
we'll then go ahead and remove the element.
So all in all, rather than just immediately remove the element when I
click on the button that says Hide, what I'd like to do is say if you click
on a button and the button is Hide, go ahead and get the parent element--
not the Hide button, but the post itself--
set its animationPlayState to running, meaning run the hide animation,
and then add an event listener to the parent, to that post as a whole,
to say when the animation is over, go ahead and remove that entire post
from the DOM altogether.
So what is the effect of all of this now, of having this animation
and running it?
Well, now if I refresh the page, I see all these posts.
If I try and hide, like, Post #2, for example, you'll
see that the opacity changes and then it slowly disappears.
And then only after it's totally transparent,
the post disappears entirely.
So I can say hide Post #4, it disappears, and then Post #5
jumps up to fill its place.
And I can do that for any of these posts,
triggering the animation when I click on the Hide button.
And so this is part of the value of what animation can do,
is to be able to make our user interfaces a little more
pleasant from the perspective of the user
by not immediately getting rid of a post, but by having a nice fadeout
so it disappears nicely.
Now, even this is not perfect animation wise.
Like, one thing you might notice is that it
jumps up as soon as the post is gone.
If I hide Post #3, I hide it, it disappears, and Post #5
sort of jumps up very abruptly in order to fill its place.
What I might like is to be a little bit cleverer, to somehow shrink
the size of the post after it's gone so that the post doesn't jump into place
but it slides a little bit more naturally into place.
And so there's some additional things I can play with here.
Maybe I want to say that, all right, let me make this animation a multiple part
animation.
So here, instead of just from 0% to 100% setting the opacity from 1 to 0,
maybe in the first 75% of the animation, that
will take care of reducing the opacity, going down from 1 all the way down
to 0.
But in the last 25% of the animation, we'll still end with an opacity of 0,
but anything that creates vertical space, I want to reduce down to 0.
So the height should be 0 pixels, the line-height,
which is how high the text is, should also be 0 pixels.
And any padding I want to go away.
It turns out I've added some margin to the bottom of the post--
I want to make that go away as well.
So I want to set all of those to 0 from whatever their initial values happen
to be, that initially the height is like 100% of what the height could be.
Likewise for line-height, 100% of the parent.
Initially I have, like, 20 pixels of padding
and a margin at the bottom of 10 pixels.
And I want all of that to still be true 75% of the way through the animation.
But it's only in the last 25% of the animation
that I want to set all of these vertical height properties down to 0.
I want to remove all the height, remove the line-height,
remove all the padding.
And the effect of this is I'll have an animation now where
for the first 75% of the animation, the only thing that changes is the opacity.
The opacity goes from 1, fully visible, to 0, fully transparent.
And then in the last 25% of the animation,
the post is already transparent.
You can't see it, but it's still taking up physical space on the page.
But we're going to now reduce the height of that post
so that now you won't be able to see it at all.
So now if I refresh this page, here again are all the posts.
But now if I click Hide on a particular post,
we'll see that it first fades out, and then its height
shrinks so that the next post slides very nicely into place.
I can do that again.
Hide the post, it's transparent, and then it slides into place.
And this, again, is just an application of this idea of CSS animations,
using properties of animation to make our interfaces a little bit
nicer to use, a little bit clearer visually to the user
that one post has gone away and the rest of the posts
have now scrolled up in order to take their place.
So now we've been able to use JavaScript to create
a lot of nice user interfaces.
We've been able to create a single-page applications,
to create infinite scrolling, to be able to create some animations as well
and use JavaScript to be able to control them.
But one thing you might be realizing at this point
is that our applications are starting to get fairly complicated.
There's a lot of JavaScript code needed to manipulate
a lot of different parts of our application at the same time.
And you can imagine that as web pages start to get more complex
and as you want to start making them more interactive and more dynamic,
there's going to be a lot of JavaScript code required in order to keep
everything in sync, in order to make sure that all of the elements
are updated when they should, so on and so forth.
And it's for that reason that in recent years, a lot of JavaScript
has now turned to some JavaScript libraries or frameworks that
allow to more efficiently and more effectively
create user interfaces that are more interactive and more reactive.
And one of the most popular of these is a framework known as React.
React is a JavaScript library that is going
to enable us to be able to design user interfaces that
are very interactive, where the content of the web page updates
automatically based on some underlying state.
And what we'll do now is take a look at a brief taste of React
to get a sense for how frameworks like it can actually work
and can help us in designing some interactive and useful interfaces
for users to be able to interact with.
React is ultimately based on this idea of declarative programming,
a particular style of programming which is
different from the types of programming you
might be familiar with-- more classical programming styles like imperative
programming.
In imperative programming, you generally give the computer commands,
tell the computer what to do.
For example, if we had that counter program from before
and we wanted to update the counter from one number to another number,
in the view, like the HTML that the user sees,
we would include something like a heading that
just has the number 0 inside of it.
And then the logic in imperative programming
would take something like this form.
It would be like, all right, first document.querySelector("h1") to get
that h1 tag.
Get it, it's in our HTML.
parseInt-- we'll take the string and convert it into an integer,
and we can save that inside of a variable called num, for example.
And then after that, if I want to increase it,
I would take this variable num and just add 1 to it-- num plus equals 1,
add 1 to it.
And then if I want to update this heading in order to replace the 0 with
a 1, for example, well, then I would need to say
document.querySelector("h1"), set the inner HTML equal to that number,
for instance, in order to say, all right, num is now 1.
Go ahead and replace that in the view.
But this is a fair amount of code to do something fairly simple,
just like increase a number by 1.
And the reason why is because we've had to be
very explicit about what instructions we're giving to the web browser.
We're saying first, grab the h1, figure out what number is inside of it,
add 1 to that number, and then replace it inside of this tag.
What declarative programming is going to allow us to do
is it's going to allow us to just describe what state should
be displayed on the page in what form.
In declarative programming, in our view, like the HTML-like code
that we're going to be writing, we're just
going to say something like, h1, and then in curly braces num,
to mean fill in the number here.
And this is what the React syntax is going to look like.
And then the logic code, if we want to increment that number by 1,
is we just need to say num plus equals 1.
Add 1 to the number.
And the effect of that is that since we have declared that inside of this
heading, it should be whatever the value of the number
is, when we increment the value of number,
React is effectively just going to update the view so
that the number updates as well.
And so this will be some of the power that React gives us.
React lets us divide our application into a whole bunch
of different components, where a component is something
like this thing here that is keeping track
of some sort of count, along with a button that might manipulate
it, and then make that component based on some underlying state,
some underlying variables that represent the state of the application--
something like the current number.
And then we can manipulate that state, and when we manipulate the state,
that will have an impact on what the user actually sees,
and React will handle the process of updating that user interface for us.
There are a number of ways to get React working on our web page,
but the simplest is probably just to include these three JavaScript
packages inside of our web page.
So we're first going to include React itself,
which is going to be the library that's going to allow us to define
these components and how they behave.
Then it's ReactDOM, a special package that's
going to allow us to take React components
and insert them into the DOM, the document
object model that represents the structure of the entire page.
And then finally, Babel is going to be a package
that we're going to use in order to translate code
from one language to another.
It turns out that when we're writing React code,
we're not actually going to be writing JavaScript.
We're going to be writing in an extension to JavaScript known as JSX.
And JSX is going to be an extension to JavaScript that
looks a lot like JavaScript, but has some additional features.
In particular, it has the ability to effectively allow
us to represent HTML inside of our JavaScript code
in a way that's much easier to read and it's
going to be convenient for us to deal with.
Browsers, on the other hand, don't understand JSX automatically,
so what we're going to use is a tool like Babel
to convert that code into plain JavaScript
that our web browsers are ultimately going to be able to understand.
The best way to get a feel for this kind of thing is just to see it in action.
So I'll go ahead and create a couple of React applications,
just to get a sense for how it is that you can use
React in your own applications as well.
So let's start by taking a look at react.html.
And what I have here is the beginning of an HTML page.
Inside the head section, what you'll notice
is I've already included these three script tags.
And what these script tags are doing are just
including those three JavaScript libraries
we were talking about a moment ago.
I have a title for the page just called React.
And now let's start to fill in the body of this web page.
I'll begin by adding a div, which I'll give an ID to.
I'll call it app, but I could call it anything.
And this is where our application is going to go.
But right now I'm just going to leave it as empty.
It's going to be React's job to fill in this div with the content of our user
interface.
And now beneath that div, I'll start to write some JavaScript.
But remember, I'm not going to be writing JavaScript, per se, but rather
JSX, that extension to JavaScript.
So in this case, I'll need to add an extra attribute. type="text/babel",
and all this is doing is telling my browser that it's going to need
to translate this JSX code into JavaScript code that the browser is
actually going to be able to understand before it tries to run this code.
In practice, if you were developing a real application, what
you would want to do is you would want to do this translation ahead of time,
prior to deploying the application.
But here we're just going to translate it on the fly.
And so all of our React applications are going
to be composed of components, where a component is just some part of my web
application's user interface.
And to describe a component, I can write a JavaScript function.
So I'll create a function called App, which is
going to represent this app component.
And what's going to go inside of this app component?
Well, it's a function, so it's going to return something.
And what it's going to return is what should appear inside of that component.
And this app component could really just be, for example, a div
that says "Hello!", let's say.
And this is where the power of JSX really
starts to come in, that here I can write HTML-like syntax
inside of my JavaScript code, and JSX is going to be able to understand it.
So this function right here is a function called
App that is representing a React component,
and when this component is rendered onto my web page, it's going to say "Hello!"
So there's one last line I need inside of my JavaScript now,
and that is to actually render this component into the page.
To do that, I'll say ReactDOM.render, and the first argument
to this function, ReactDOM.render, is what component would I like to render?
The component is this app component that I just created.
And so I'll say App, again using this HTML-like syntax.
And then the second argument is, where on the page
would I like to render this component?
And I want to render this component right here on line 10, where
I have a div whose ID is app.
So I'll need to add some JavaScript code to find that particular div.
And to do that, I can just say document.querySelector and then
#app to say find the element with an ID of app,
and that is where I would like to render this app component.
So I first created this empty div, then I defined this function
representing a React component.
And then after that, we're going to render that component
inside of the HTML page itself.
So now if we were to open a browser and see what this page actually
looks like--
I'll make the text a little bit bigger--
you see that we actually do see the word "Hello!"
And if I make a change to the component and refresh the page,
it will change the page as well.
So if the component instead displayed "Hello, world!",
well, then I refresh the page and the page now also says "Hello, world!"
And because this is JavaScript, I can add JavaScript code to the function
just as I could with any function in JavaScript.
Imagine, for example, that I had some variables like--
let's create a variable x, which is equal to 1,
and a variable y, which is equal to 2.
Inside of this div, rather than just render some text,
I can use curly braces to say plug in the value of some JavaScript
expression.
I could plug in the value of x plus y, for example,
and now by including x plus y in these curly braces,
JavaScript is going to evaluate what is x plus y
and display that inside of the div instead.
And so now when I refresh the page, you see
that the page just says 3, for example.
And so that's the basics of React.
We create these components and then render those components,
all using the power of JavaScript.
But where React starts to get more powerful
is when we can reuse components.
The whole idea of a component is it represents some part of the user
interface, and I could reuse that same component
across multiple parts of the interface as well.
Imagine, for example, that inside of my app component,
I was going to render a div that had three headings, each of which
said "Hello!"
So there's one heading, there's another one,
and we'll add a third one there as well.
So I have a div with three headings inside of it,
and we can see what that looks like.
Each one of them says "Hello!"
But there's some repetition here.
I'm having to use this h1 tag three times,
all to create exactly the same UI element on the page.
This is a case where I can create a separate component
and then just reuse that component, rather than have
to repeat myself multiple times.
So how could I do that?
Well, remember that in JavaScript, we can write a function
to represent a React component.
So I'll add another function here and I'll
call that function Hello, because it's going
to represent this hello component.
And this Hello function is also going to return something,
and what it's going to return is a heading, an h1, that just says "Hello!"
And so now inside of my app component, rather than render three separate h1s,
I can simplify this a little bit to just say "Hello."
Here, I'm saying, go ahead and render a hello component here,
render a second one and a third one after that.
Each time I render a hello component, it's
going to display as this heading that just says "Hello."
So I refresh the page and nothing changes.
I still see three headings, each of which says "Hello!"
because inside of my app component, I'm rendering this hello component
three times and each time, it's going to display this h1.
But where components start to get more powerful
is when they're not always displaying the same information every time,
but when we can parameterize those components with properties.
Or as React simplifies them, props, short for properties.
So what would that mean?
We see that HTML elements can take attributes.
Likewise, React components can take properties, where maybe I don't just
want to say hello, but I want to say hello to someone-- hello to Harry
or to Ron or Hermione, for example.
So I could say Hello name="Harry", using syntax much like an HTML attribute,
then here I say Hello name="Ron", and then finally Hello name="Hermione".
And so now my hello component is accepting this prop,
this property called name, which is different for all three
of these components.
And so inside this Hello function now, I would like the Hello function
to take advantage of these properties, of these props.
And so I'm going to add an argument to the Hello function.
That argument is conventionally just called props.
And now, instead of just saying Hello, I'm going to say Hello comma.
And then remember, to plug in a JavaScript value, I use curly braces.
And inside of those curly braces, I can say props dot and then
whatever the name of the property is.
In this case, the name of the property is just name.
So I can say props.name to say whatever the name prop is,
go ahead and plug that in right here inside of the hello component.
So the hello component is going to say Hello comma, and then someone's name.
So I can save that, refresh the page.
And now I see "Hello, Harry!", "Hello, Ron!", and "Hello, Hermione!"--
three different components, each of which
is still just this hello component, but we're
rendering it with different props, one time with the name of Harry,
one time with the name of Ron, and one time with the name Hermione.
And so this is where components can start to get a little bit different.
By passing in different props into those components,
we can decide how that component is ultimately going to render.
But let's add to this a little bit and start to add state
into our React components as well.
And state is going to mean any kind of data
that we want to store inside of the component itself.
And for this, let's try to recreate the counter application
that we created when we first introduced JavaScript, where we were really
just creating a button that allowed you to count,
and it counted up from 0, 1, 2, 3, 4, et cetera.
So to do this, let's create a new file.
I'll create a new file and just call it counter.html.
And we can start just by copying the contents of react.html
into our counter.html file.
We're still going to use the same script tags
and we can still have an app component, but what's
going to be inside this app component is going to be a little different.
I'll change the title of the page to be Counter instead of React.
So what goes inside of the app component?
Well, if we're going to do this counting,
we need a div that's going to display the number that we've currently counted
to, something like 0 to start with.
And we're going to need a button.
This button is just going to be Count, which
will be the label for that button.
So a div that just says 0 and a button that says Count.
And now if I open up counter.html--
I'll make it a little bigger--
you can see that I have this number 0 here and a button that says Count.
Of course, right now, clicking on the button
doesn't do anything because I haven't written any JavaScript
code to say what should happen when this button is actually clicked on.
But before we get there, let's modify this program a little bit.
Right now, I've written the number 0 directly into the div itself,
but it's not always going to be 0.
Eventually, as I start counting by pressing that Count button,
that number is going to change.
So what I'm going to do now is factor this 0 out
into what's known as state inside of my React component.
And here, one way to create state in my React component
is to use a special function inside of React called useState.
This is one example of a React hook that allows
me to add some additional functionality into my React component.
And the argument to React.useState is going
to be the initial value of that state.
I'm going to start counting and I want to start counting from the number 0,
so I'm going to include the number 0 as the argument to this useState function.
So we're going to start counting at 0.
And what this useState function returns is really an array of two things.
It's going to be a variable that I can give a name to--
I'll call it count--
and also a function that I'm going to call setCount.
And that function is going to allow me to set the value for the state,
if ever I need to change the state at some point in the future.
So this useState function accepts 0, the initial state, as its argument.
And then I get two things back.
I get the state variable itself, called count,
and I get a function for changing that state when I need to.
So now instead of rendering a 0 inside of the div
just by writing the number 0, I'm going to instead in curly braces
go ahead and render whatever the value of count is.
Initially, it's going to be 0, but eventually, that number might change,
and I want my UI to reflect the changes in the underlying state.
So right now if I refresh the page, it still says 0,
because the initial state was set to 0, but I could change that.
If initially that initial state was some other value, I could refresh the page
and see a different value appear for the count instead.
Whatever the value of this count variable is in the state,
that's going to be what the user is going
to see when they're looking at my user interface
and when they see my component.
So now let's make this button actually do
something, because right now, the number is never changing.
To do that, I can add an onClick handler.
And notice one difference between onClick in React
and onclick as we traditionally used it in JavaScript--
I'm using this capital C, and that's just a common React convention
when we're defining event handlers.
And here, I'm going to say onClick, and then in curly braces
the name of a function-- a function that I would like
to run when this button is clicked.
And I can call that function whatever I'd like.
I'll call it updateCount, for example.
And now what I need to do is define a function called updateCount.
And I'm going to define that function inside of this React component,
inside of my app function.
It turns out in JavaScript, you can have functions that
are defined inside of other functions.
So I'll define this function, called updateCount,
and what do I want the updateCount function to do?
Well, what I'd like to do is just increase count by 1.
And you might think that I could do that just
by saying count equals count plus 1, but it turns out
you can't quite do that in React.
In React, whenever I'm using this useState,
if I want to change the state, I have to use this function
that useState provides to me for whenever I
want to set the new value of the state.
So rather than count equals count plus 1, I have to use the setCount function,
and the argument to setCount is going to be count plus 1.
So setCount is this function that is going to change the underlying state
inside of my component, and the argument is what should the new state be.
And in this case, it's just going to be count plus 1,
one more than whatever the count was before.
So I can save that.
And I'll go ahead and refresh the page.
It starts at a 0, but every time I click on this Count button,
you'll notice the count increases by one.
And again, I have no code that's saying go into the div and change whatever
is inside of the div.
All I have inside of this div is this reference to this state variable count.
And whenever the state changes, JavaScript and in turn React
knows that what React needs to do is to recreate this component,
rerender the component by displaying the new value of this state variable.
And then when the button is clicked on, we're
able to run this function to change the value of that underlying state.
So by taking advantage of these React components with state,
we can start to represent information inside of our components
and then define what our component is going to display as,
just by representing HTML in terms of that underlying state,
deciding how we should use that state in order
to render an interface that the user is ultimately going to see.
So let's now try and put these pieces together
and create a web application that uses these abilities of React
to define state and to manipulate that state and in turn,
update a user interface based on changes that
are happening to that underlying state.
And we'll create an application that will just
display some simple mathematical questions to the user
and quiz the user on some basic addition facts, for example.
So let's create that application.
I'll create a new file and call it addition.html.
And inside of addition.html, I'll start again
just by copying the contents of this counter.html file,
because the framework, the structure of this page will be similar.
But I'll go ahead and clear out what's inside of my app component,
at least for now.
And so what would I like for my app component to render?
Well, let's go ahead and render a div.
And if I want to create an application that's
going to ask the user some mathematical questions
and then prompt the user to type in an answer,
there are at least two parts of this user interface that I'm going to need.
I'm going to need a place to display the addition fact answer, like what
is 1 plus 2, for example.
And then I'll need an input field where the user can type in their response
to that question and then see if they got the question right or wrong.
So inside the div, I'll start by creating
a div that displays the question itself, something like 1 plus 2.
And then beneath that, I'll just add an input field.
Eventually, we'll add more to this user interface, but for now,
all we really need is a div that displays
the mathematical question and an input for the user to type in their response.
So now if I go ahead and go to addition.html, here's what I see.
I'll make it a little bit bigger.
I see 1 plus 2, and then an input field where the user
could start to type in their response.
But just as we did before, I don't want to literally write the numbers
1 and 2 into what I'm returning.
Instead, I want these 1 and 2 to be based on some underlying state
inside of my application.
The application is going to maintain state
about what two numbers to add together, and then it's
going to display a user interface based on that state.
So what could I do here?
Well, one thing I could do is again use React.useState, start this number off
as 1, and maybe call this num1 and then a function to set number 1.
And then I could do it again.
Let's create num2 and set num2 to be React.useState(2).
And I could have two different pieces of state, num1 and num2, each of which
has a different function, setNum1 and setNum2, that
are each representing the two different numbers that I
would like to add together.
But already this is starting to get messy and over time,
as I add more different pieces of state to the application--
as we'll see in just a moment--
the state might start to get more and more complex,
with more and more different functions and variables.
So it's often helpful, and a common practice in React,
to combine multiple pieces of state just into one JavaScript object that's
maintaining all of the different pieces of state for this particular component.
And to do that, I'll again use React.useState.
But instead of setting the state initially to be a number like 1 or 2,
it's instead going to be a JavaScript object that
has keys and values, where I could say let num1 be the number 1
and let num2 be the number 2.
Much like a dictionary in Python, for example,
where I have multiple different values, num1 and num2, all together
inside of the same object.
And I can call that state and have a variable--
and have a function called setState that is going
to update the value of that state.
And so rather than have to have all of these different variables,
I can simplify a little bit to just state and a function to set the state,
and the state now has these two different pieces, number 1
and number 2.
And so now instead of rendering literally the number 1,
using curly braces I can say state.num1.
And instead of rendering literally the number 2,
I can say state.num2, drawing upon that state
to decide what it is going to appear inside of the user interface.
And so right now, the page appears no different.
But if I were to change those initial values of the state,
maybe make it 2 and 4, for example, and then refresh the page,
well, now it displays as 2 plus 4.
And so that's helpful.
We now have a user interface where the numbers are based on the state.
But now what I'd like to do is add the ability
to keep track of what the user typed in so we can tell if the user correctly
typed in the answer to this mathematical problem.
And how would I do that?
Well, the state represents any information
that we need to keep track of inside of this component.
And so in addition to storing the two numbers inside of the state,
I likely also need to keep track of a third piece of information,
which is the response--
what did the user type in into this text field?
And so we'll add a third part of the state called response that initially
will just be the empty string.
It will just be nothing.
And then this input field, I'm going to give it a value,
and the value is going to be state.response.
Whatever the user typed in as the response,
that's stored inside of the state, and that
is going to be the value of what shows up in the input field.
And so that way, whatever is in the input field will have access to it
inside of this state.response variable.
But there is a problem, and here's the problem.
I'll try refreshing the page, I'll go into this text field.
And let's say I know the answer.
I know 2 plus 4 is equal to 6.
I'm now going to press 6 on my keyboard.
But as I press 6 on the keyboard, nothing's happening.
No 6 is appearing inside of the text field,
even though I am pressing the key on the keyboard.
So why is that?
Why is the text field not updating?
Well, the reason is the value of the input field, whatever
appears in the input field, is this value, state.response,
and state.response is always this empty string
and never changing what state.response is equal to.
And so I need to change this a little bit.
I need to add as an attribute to this input field onChange,
meaning when the input field changes, I need to do something.
And I'll call a function that I can call updateResponse.
But again, I could call that update function whatever I'd like.
It's just the name for the function that's
going to run whenever something changes in the input field.
So let me now define that updateResponse function.
I'll define a function called updateResponse.
And because it's an event handler, it can accept an argument,
which is the event itself--
the fact that something has changed inside of the input field.
And when I have access to this event, it turns out
that if I want to figure out what it is the user has typed into the input
field, I can get at that with event.target.value.
And I'd only know that by looking at it in the documentation.
But what I'd like is for event.target.value
to be the new value for this response.
And so what I'd like to do is do something like this-- setState.
And what should the new value of the state be?
Well, I would like for a response to no longer be the empty string,
but to now be event.target.value.
And that is going to be the new value for response.
But I'm not quite done yet, because state doesn't just
have response as one of the parts of the state.
The state also has num1 and num2, and those two pieces
aren't really changing.
So I could say, all right, num1 is just going to be whatever state.num1 was.
That's not changing.
And num2 is going to be whatever state.num2 was.
That's not changing.
The only thing that's changing is the response.
But this is starting to get a little bit verbose,
and especially if I start adding more and more different pieces to the state,
it's going to become difficult to manage if I constantly
have to repeat myself for all of the parts of the state that
aren't changing.
Ideally, what I'd like to do is just specify
the parts of the state that will change and ignore everything else.
And so one shorthand way to do that in JavaScript
is to use what's known as the spread operator.
And it looks like this-- dot dot dot and then state.
And what this is saying is just use the existing
values of the state for everything else, like num1 and num2.
The only thing to override is the new value for the response.
And so this syntax here is my way of saying,
I would like to update the state.
Everything should stay the same except for response,
which is now going to be event.target.value--
in other words, whatever it is the user typed in into that input field.
And so I'll go ahead and refresh the page,
and now if I type a number, like 6, you actually
see that number appear in the input field.
So that's great we've now displayed a question where the numbers are stored
in the state, and the user can type in a response where that response is also
stored in the state.
Now what I'd like is when the user presses the Enter key
on their keyboard, we check--
did they get the answer right or did they get the answer wrong?
And so how would I do that?
Well, the first thing I need to do is in this input field,
somehow detect when a key is pressed.
When a key is pressed, what I'd like to do
is check to see if it was the Enter key.
And if it was the Enter key, then let's go ahead
and check to see what the actual sum of the two numbers is
and see if the user got that right or wrong.
So let's add an event handler.
onKeyPress is going to be equal to something.
Again, I can name this function whatever I'd like.
I'll call it inputKeyPress, but again, I could name that anything.
And now let's define that inputKeyPress function.
So up above, I'm going to define this function called inputKeyPress.
Again, it takes that event as its argument.
And this event is going to happen any time a key is pressed,
regardless of whether it's a letter or a number or the Enter key.
And so I want to check to make sure that the key is actually the Enter key.
That's the only time that I want to now check
to see if they got the question right or wrong.
So we'll add here a condition.
It's just JavaScript, so I can say if event.key
is equal to Enter, well, then, let's go ahead and check.
And otherwise, we don't have to do anything.
I don't need an else case here, because nothing
should happen unless it's the Enter key that was actually pressed.
And so now how do I check to see if the user got the answer right or wrong?
Well, inside of state.num1 is the first number, and inside of state.num2
is the second number.
So I could have a condition that checked if state.num1 plus state.num2 is
equal to state.response, which is what the user typed in into the input field.
But that doesn't quite work, because state.response, that's a string.
The user doesn't necessarily have to type in numbers.
It's possible the user is going to type in some letters
instead, for example, or other characters instead.
And so what I'm going to do first is convert the response into an integer,
if we're able to do so.
So I'm going to define a variable called answer using the JavaScript function
parseInt that takes a string and tries to convert it into an integer.
So we're going to parse the int state.response.
And now we can check.
If number 1 plus number 2 is equal to the answer,
well, then this means the user got the question right.
And else if the sum is not equal to the answer,
that means the user got the question wrong.
And so now what I could do is handle those two different scenarios.
In one case, the user got the question right and we should do something,
and in another case, the user got the question wrong
and we should do something else.
And we're making that decision by looking
at the state of the application, by looking at what two numbers we're
supposed to be adding and looking at what
the user typed in as their response.
So what should we do when the user gets a question right
or gets a question wrong?
Well, maybe this game is going to keep score
by maintaining a number for how many questions the user has gotten right.
And every time the user gets a question right,
we could increase that score by 1, and any time
the user gets a question wrong, we could decrease that score by 1, for example.
So how would we do that?
Well, the score is some piece of state inside of the application,
and so we're going to need to add to the state.
Right now in the state, we're storing the number 1, a number 2,
and a response.
I'll add to that a score where the score is going to start out as just 0.
And we can render that score on the page.
If I scroll down to where we're returning the div to render,
let's add another div that says the score is,
and then using curly braces, plug in whatever the value of state.score is.
Whatever the score is, let's figure that out from the state
and let's display that in the user interface.
So now this user interface shows not only a question
and an input field, but also a score.
And the score starts out as just the number 0.
So let's now go back to this function.
When a key is pressed, if it's the Enter key,
let's check to see if they got the answer right or wrong.
We check-- did the user actually get the question right?
If so, what should we do?
Well, we should increase the score.
And how do we do that?
We do that by calling the setState function.
All of the state is going to be the same, so using
that dot dot dot state spread operator.
The only thing that's different is the score
is going to be state.score plus 1.
So we're updating the state to increase the score by 1.
And if the user gets the question wrong, let's set the state
to be dot dot dot state, and then the score
is going to be state.score minus 1.
So if the user gets the question right, we increase the score by 1.
Otherwise, we decrease the score by 1.
And let's test that to see what it actually looks
like when we try this in the user interface.
I'll refresh the page--
2 plus 4.
If I type in the correct answer, 6, press Return, the score increases by 1.
If I typed in the wrong answer, let's say 8, press Return,
the score decreases by 1.
So this appears to work.
Depending on whether I get the question right or wrong,
the score is able to update, increasing or decreasing
based on the result of that condition.
Now, this game is pretty easy to get a high score on right now, because I
can just keep pressing return over and over and over,
and the question's never changing.
My response is already there.
And so the score keeps going up and up and up.
So let's make the game a little bit more interesting.
Every time the user gets a question right,
let's display a new question for them to answer.
And how would we do that?
Well, the question that's displayed to the user
is based on two underlying pieces of the state of the component.
It's based on state.num1 and it's based on state.num2.
So if I want to change the question, all I have to do
is when the user gets the question right and I'm
updating the state, instead of only updating the score,
let's also update num1 and num2.
And I could set these to be specific values,
maybe like 5 and 10 for example, but let's make it more interesting
and display a random number every time.
We'll generate a random number, and so the user
will be adding two random numbers together every time they
get a new question right.
How do we generate a random number?
Well, Math.random is a JavaScript function
that generates a random number between 0 and 1.
We can multiply it by 10, so now we're getting a number between 0 and 10.
But we don't want any decimals to appear in the number.
So we'll go ahead and take the ceiling of the number, Math.ceil,
to say if the number was, like, 5.8, we'll just go ahead and round that up
to 6, for example.
And we'll do the same thing for number 2.
We'll take the ceiling of math.random times 10.
So every time the user gets a question right,
we'll update num1 and num2 to be new random numbers generated just
like this.
And so let's go back and try it again.
We see 2 plus 4.
I type in the correct answer, 6, and the question changes.
8 plus 5.
I type in the correct answer.
Press Return.
My score increases, and the question changes again.
This time if I get the answer wrong--
I type in 10, for example--
watch my score decrease.
It went from 2 down to 1.
But the question didn't change.
Now I get another opportunity to try to answer this question.
And when I answer it correctly, the score increases again from 1 to 2.
So this game is starting to come along now.
It's keeping track of my score, it's displaying different questions.
There is at least one user interface quirk right now,
and that is the fact that at the moment, when I get a question right and press
Return--
I type in 6 and press Return--
the 6 still stays there.
Ideally, I get a new question.
I'd like to clear out the response so the user can just
type in whatever the new answer is, rather than have
to delete whatever they typed in before and then type in a new number.
So how could we do that-- reset whatever is inside of the input field?
Well, what's typed into the input field is stored inside
of the state of my component.
It's stored inside of state.response.
And so if I wanted to change that, all I would have to do
is say, let's change the response to be the empty string.
When the user gets a question right, we're
going to update these two numbers, increase the score,
and also clear out the response so that it's just the empty string.
And I can do the same thing if the user gets a question wrong--
decrease the score by 1, but also clear out
that response back to the empty string so that there's nothing there.
And so now we get a question, I type in an answer, press Return,
and the input field clears out, I get a new question,
and the score increases by 1.
Four separate pieces of state all changing at the same time,
and that gets reflected in the user interface that I'm now able to see.
So I type in another value and the score increases and everything updates again.
All right, so that's definitely progress.
One other user interface quirk that I noticed
here is that the input field by default isn't automatically selected
where I would have to go in and click on the input field
in order to highlight it so that I can start typing in my response.
I can fix that pretty easily if I scroll down to where the input field is.
We'll add an autoFocus attribute and just set
that to be true so that the input field automatically focuses when
I load the page for the first time.
So now I refresh the page.
The input field is already highlighted, and immediately I
can start to try to play this game.
So now that we have the basic functionality of this app working,
let's try and improve the CSS so that the game looks a little bit nicer.
I'll scroll up to the top of the page and add a style tag
to the head section of my HTML page.
And I'd like for this entire app to be centered,
so I'll say text-align is going to be center.
And I'm going to set the font-family to be sans-serif, because I prefer
that font for this particular game.
So I refresh the page and now everything is centered,
and the font is different than what the default was.
And what else would I like to have changed?
Well, this problem, 2 plus 4, maybe I'd like for that to be bigger.
I'd like the problem to be big, and the score
beneath that, that can stay the same size that it is right now.
So how would I do that?
Well, if I go back to the HTML here, I'll
go ahead and give this div where I'm displaying
the problem, number 1 plus number 2, I'll give it an ID of problem.
And then if I scroll back up, I'll say for the element with an ID of problem,
let's go ahead and set the font size to be 72 pixels, for example,
just to make it bigger.
And so now I see a big math equation, 2 plus 4,
for example, the input field, and then the smaller score beneath it.
So that's a nice UI enhancement a little bit,
and now I can play the game, get a question right,
and the score increases.
I get a question wrong and I get to try again.
But maybe I'd like to offer more of a visual indication
that the user got a question wrong.
Maybe any time the user gets a question wrong,
I'd like to change the color of this text.
Instead of being black, instead it should be red
when the user gets a question wrong.
And how could I go about doing that?
Well, we can change the color of something just by using CSS.
If we had, like, a class called incorrect, for example.
If I scroll down here and give this div a class name, which
is how you add a class in React, of incorrect,
then I could use this class name to style it as red or not red.
So I could say anything that has a class of incorrect, let's go ahead
and give that a color of red.
And so now, because I gave this problem a class of incorrect
and I said turn all incorrect text to be red,
we now see this text appear as red.
But this, again, is not quite what I want.
I don't want it to be red all of the time.
I only want it to be red when the user has just gotten the question wrong,
when they were just incorrect in answering a mathematical question.
And so how could I represent that information inside of my application?
Well, I'm going to need some additional state.
State, again, is any information that I need
to keep track of inside of my component.
And now it seems that in addition to the response and the score and the numbers,
I also want to keep track of did the user just answer a question incorrectly
or not?
So I'll add another piece to the state.
I'll call it incorrect.
And initially, it will be false.
They didn't just get something incorrect.
And now here, if I scroll down to this className,
rather than have it be incorrect all the time, let me add in curly braces
an expression.
I'll say if state.incorrect is true, using the ternary operator
with a question mark, then the class should be incorrect.
But otherwise, it shouldn't have a class of incorrect.
It'll just be the empty string.
And so this expression here allows me to change the class of an HTML element
based on the underlying state.
If state.incorrect is true, then this div will have a class of incorrect,
and otherwise, it won't.
And so now when I load the page for the first time, the text appears as black.
And what I need to do is when the user gets a question wrong,
I need to change the state to indicate that they just got a question wrong.
How do I do that?
Well, here is the setState call when the user gets a question wrong.
And in that case, I'll go ahead and set incorrect equal to true.
And when the user gets a question right, we'll
go ahead and set incorrect equal to false.
We're modifying this one additional piece of state
based on whether the user got the question right or wrong.
So now if I load the page, answer a question correctly,
the score increases and I get a new question.
But if I answer a question incorrectly and press Return,
you'll notice the score decreases, the input field clears out,
and the text changes color because I changed
the value of that incorrect part of the underlying state and based on that,
we were able to see the text color change as well.
If I now get a question correct, press Return,
the text color changes back to black and the score increases.
And let's now add one final piece of state or one final change to the UI
for this application.
Let's give me a way to win this game.
Maybe once I get to a score of 10 by answering 10 questions correctly,
then we're going to win the game.
And how could I do that?
Well, remember that each React component can just be a JavaScript function.
And this function is just immediately returning this div,
but it's a function, so I can add additional logic to it.
I can say if state.score is equal to 10, for example, then
rather than render the old div, let's return a new div.
This div is just going to display something like "You won!"
And so that I can style it, I'll give it an ID.
The ID will be winner.
And if the ID is winner, let's go ahead and make the font size 72 pixels
and let's make the color green if I win.
So I added some CSS just to style it, but really, the only new logic
is further down below, where I'm here saying check the state.
If the score is 10, well, that means we win,
so instead of returning the new problem, just
return a div that says you won the game.
So let's try that now
I get these questions.
Every time I answer a question, you're noticing that the score
is going to increase by 1.
And every time we're generating new random numbers
to display as what's going to appear in the user interface.
And once I get to question number 10, if I answer it correctly, press Return,
the entire UI changes.
Instead of the problem and an input field and a score,
I just see in green large text that I won.
And again, I was able to do that by looking here at this condition, where
we're looking at the value of the state, and if the state is 10,
we're deciding what to render.
And this, again, is one of the great powers of React,
this ability to use this underlying state
and based on the value of the underlying state,
decide what it is the user should see in their user interface.
And React is just one of many libraries that do this type of thing.
Other popular ones include Angular and Vue, where all of these
are just these web frameworks that make it easier
to be able to create applications that are able to respond
to some underlying state so that you, the programmer,
don't have to worry about constantly having to manipulate
various different parts of the page, especially
as you imagine websites like Facebook or Twitter,
where there are many things happening on the page at the same time.
Every time a new tweet comes in, you might get a notification
and see a new post in your main area of your news feed.
So these are the types of things that you might want the application
to be able to more easily handle for you, where you describe what
the state is, you describe what the page should look like based
on that underlying state, and let the library,
whether it's React or something else, begin to handle
the process of doing that for you.
And the world of user interfaces is changing pretty quickly--
that a lot changes in user interfaces in terms of the technologies
and the tools that are quite popular.
But they're really based on the same set of underlying ideas,
the idea that we can use JavaScript in order to manipulate what it is the user
sees on their page, in order to detect what's
happening based on particular events, like scrolling
to the bottom of the page or typing something into an input field,
and then responding to those particular events
by providing some sort of function that gets called
any time a particular event happens.
By mixing that in with other features, like the ability
to asynchronously request information from an external server or the ability
to do computations based on the values of the state, like we saw within React,
we have the ability to create very interesting,
engaging, dynamic user interfaces very, very quickly, all just
using the power of combining Python and JavaScript.
That was Web Programming with Python and JavaScript for today.
We'll see you next time.