diff --git a/common-content/en/module/js2/browser/index.md b/common-content/en/module/js2/browser/index.md index a3faa5832..cb8b64399 100644 --- a/common-content/en/module/js2/browser/index.md +++ b/common-content/en/module/js2/browser/index.md @@ -17,6 +17,6 @@ emoji= '🧩' User interfaces provide the gateway between a user and a complex application. When navigating the internet, we continually interact with web pages to access data and interact with complex web applications. -A {{}} provides a user interface to interact with web pages.{{}} is capable of fetching HTML documents from a server, and then rendering the document to create a user interface. If a user visits a website and gets a plain HTML document back, we say this content is static. +A {{}} provides a user interface to interact with web pages.{{}} is capable of fetching HTML documents from a server, and then rendering the document to create a user interface. If every time a user visits a website, they get the same plain HTML document back, we say this content is static. By static, we mean that the server's job was just to hand over the HTML document, and then the browser takes over. A user may interact with the browser's interface, e.g. to scroll, type in a text field, or drag-and-drop an image around, but this is done purely by interacting with the browser - the browser won't talk to the server about this. diff --git a/common-content/en/module/js2/calculating/index.md b/common-content/en/module/js2/calculating/index.md new file mode 100644 index 000000000..0f3674d12 --- /dev/null +++ b/common-content/en/module/js2/calculating/index.md @@ -0,0 +1,49 @@ ++++ +title = '🧮 Calculating the remaining characters' + +time = 10 +facilitation = false +emoji= '🧩' +hide_from_overview = true +[objectives] + 1='Access properties representing HTML attributes' +[build] + render = 'never' + list = 'local' + publishResources = false + ++++ + +We want to implement Step 3: Calculate the number of characters left. + +Let's break down Step 3 into sub-goals: + +```mermaid +flowchart TD +A[Step 3.1: Get the character limit] --> B[Step 3.2: Get the number of characters already typed] --> C[Step 3.3: Subtract already typed from the limit] +``` + +## Getting information from the DOM + +We have seen that the DOM exposes live information about HTML elements in the page via properties on the objects it returns. + +We wrote `textarea.value` to get the characters already typed. This solves Step 3.2 - we can write `textarea.value.length`. + +We can also access the character limit, because it's defined as the `maxlength` attribute of the HTML textarea. + +In the Dev Tools console, if you type `textarea.max` you should see autocomplete for a property called `maxLength`. + +Most HTML attributes are exposed in the DOM as a property with the same name (but in camelCase). Let's try: + +```js +console.log(textarea.maxLength) +``` + +Now that we have the character limit (from `textarea.maxLength`), and the number of characters already typed (from `textarea.value.length`): + +```js +const remainingCharacters = textarea.maxLength - textarea.value.length; +console.log(remainingCharacters); +``` + +Try typing in your textarea, then running this in the Dev Tools console. diff --git a/common-content/en/module/js2/character-limit/index.md b/common-content/en/module/js2/character-limit/index.md index 725f63bad..ca926d9a9 100644 --- a/common-content/en/module/js2/character-limit/index.md +++ b/common-content/en/module/js2/character-limit/index.md @@ -1,5 +1,5 @@ +++ -title = '🛑 Character limit' +title = '🛑 Implenenting a character limit' time = 20 facilitation = false @@ -33,7 +33,12 @@ We can define _acceptance criteria_ for this component: > _Given_ an textarea and a character limit of 200 > _When_ a user types characters into the textarea -> _Then_ the interface should update with how many characters they've got left +> _Then_ the interface should update with how many characters they've got left. + +> _Given_ an textarea and a character limit of 200 +> _When_ a user has already typed 200 characters into the textarea +> _And_ the user tries to type another character +> _Then_ the extra character should not get added to the textarea. ### 🏁 Starting point @@ -50,8 +55,8 @@ In the user interface, we will start off with some static html:

Example character limit comment component

-
-``` - -{{}} - -{{}} - -Typing in to the `textarea` element, we should see a string like `"You have 118 characters left"` printed to the console each time a key is released. However, we have one final step: we must now **update** the DOM label with the information about how many characters are left. +Typing in to the `textarea` element, we should see the page get updated to say e.g. "You have 118 characters left". diff --git a/common-content/en/module/js2/dom/index.md b/common-content/en/module/js2/dom/index.md index a688f7a86..8ec938bbb 100644 --- a/common-content/en/module/js2/dom/index.md +++ b/common-content/en/module/js2/dom/index.md @@ -1,5 +1,5 @@ +++ -title = '🌲 Interacting with the page' +title = '🌲 The DOM' time = 20 facilitation = false @@ -13,7 +13,7 @@ emoji= '🧩' +++ -Let's consider the starting html. We need a way of interacting with the elements of this page once it is rendered. +Let's consider the starting HTML. We need a way of interacting with the elements of this page once it is rendered. ```html @@ -26,10 +26,10 @@ Let's consider the starting html. We need a way of interacting with the elements

Character limit

-
@@ -46,13 +46,13 @@ When we use a web browser, it takes this HTML document, and provides us with an ### Document Object Model -When the browser first renders a web page it also creates the DOM - short for {{}}The **D**ocument **O**bject **M**odel is a data representation of the content in a web page. All html elements are represented as objects that can be accessed, modified and deleted.{{}}. +When the browser first renders a web page it also creates the DOM - short for {{}}The **D**ocument **O**bject **M**odel is a data representation of the content in a web page. All HTML elements are represented as objects that can be accessed, modified and deleted.{{}}. Just like a web browser provides us a visual interface, the DOM is an interface. But it is not an interface for humans to see and interact with, it is an interface for JavaScript to interact with. We can write JavaScript programs to interact with the Document Object Model so we can make the page interactive. {{}} -{{}} +{{}} We can use Dev Tools to inspect the DOM and look at the elements on the page. Use Dev Tools to inspect the character limit component from earlier. diff --git a/common-content/en/module/js2/events/index.md b/common-content/en/module/js2/events/index.md index 63b9e816f..1c2681d13 100644 --- a/common-content/en/module/js2/events/index.md +++ b/common-content/en/module/js2/events/index.md @@ -1,5 +1,5 @@ +++ -title = '🎬 Events' +title = '🎬 DOM events' time = 60 facilitation = false @@ -18,16 +18,27 @@ In the case of the ` textarea` element, we want to update the `p` element text * ```mermaid flowchart TD -A[Step 1: Define the character limit] --> B[Step 2: Access the textarea element] --> C[Step 3: Calculate the number of characters left] --> D[Step 4: Update the interface with the number of characters left] +A[Step 1: Define the character limit of 200] +Initial[On first load] --> B +Event[When the content changes] --> B +B[Step 2: Access the textarea element] --> C[Step 3: Calculate the number of characters left] --> D[Step 4: Update the interface with the number of characters left] + +classDef hidden display: none; ``` However, we're missing a step in our plan. We need to find a way of running some code in response to an **event**. {{}} -An [event](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events) is something that occurs in a programming environment. +An [event](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events) is something that occurs in a programming environment that can be observed or responded to. {{}} -Events are things that happen in the browser, which your code can ask to be told about, so that your code can react to them. In a browser context, an event could be a user clicking on a button, a user typing something into a textarea box, a user submitting a form etc. Not all events are in response to user actions, for instance there is an event for the browser finished initially rendering the page. You can find a [complete reference all the different event types](https://developer.mozilla.org/en-US/docs/Web/Events) on MDN. +Events are things that happen in the browser, which your code can ask to be told about, so that your code can react to them. In a browser context, an event could be: +- a user clicking on a button +- a user typing something into a textarea box +- a user submitting a form +- and so on. + +Not all events are in response to user actions. You can think of events as "interesting changes". For instance, there is an event for the browser completing its initial render of the page. You can find a [complete reference all the different event types](https://developer.mozilla.org/en-US/docs/Web/Events) on MDN. When a user presses a key in our `textarea`, the browser will create an event. If we asked the browser to tell us about it, we can respond to it. So we can update our plan as follows: @@ -35,10 +46,11 @@ When a user presses a key in our `textarea`, the browser will create an event. I flowchart TD A[Step 1: Define the character limit] --> B[Step 2: Access the textarea element] --> C["`**Step 3: Ask to be notified when a user presses a key**`"] +Initial[On initial page load] --> E D["`**Step 4: When the browser tells us a user has pressed a key**`"] --> E[Step 5: Calculate the number of characters left] --> F[Step 6: Update the interface with the number of characters left] ``` Notice a few things here: -- There's no arrow between Step 3 and Step 4. The trigger for Step 4 is _a user doing something_ - if the user doesn't type anything in the textarea, Step 4 will never run (and neither will Step 5 and Step 6). -- _We_ don't run Step 4. The browser runs Step 4. In Step 3 we asked the browser to do something for us in the future. This is something new - up until now, _we_ have always been the ones telling JavaScript what to do next. +- There's no arrow between Step 3 and Step 4. The trigger for Step 4 is _a user doing something_. If the user doesn't type anything in the textarea, Step 4 will not run after the first load (and neither will Step 5 and Step 6). +- _We_ don't run Step 4. The _browser_ runs Step 4. In Step 3 we asked the browser to do something for us in the future. **This is something new.** Up until now, _we_ have always been the ones telling JavaScript what to do next. diff --git a/common-content/en/module/js2/plan/index.md b/common-content/en/module/js2/plan/index.md index a1685fcd9..2096ad93c 100644 --- a/common-content/en/module/js2/plan/index.md +++ b/common-content/en/module/js2/plan/index.md @@ -4,6 +4,7 @@ title = '🧭 Breaking down the strategy' time = 20 facilitation = false emoji= '🧩' +hide_from_overview = true [objectives] 1='Break down a problem into a series of steps' [build] @@ -20,4 +21,20 @@ flowchart TD A[Step 1: Define the character limit of 200] --> B[Step 2: Access the textarea element] --> C[Step 3: Calculate the number of characters left] --> D[Step 4: Update the interface with the number of characters left] ``` +There are two times we may want to do this: +1. When the page first loads we should show the _initial_ limit. +2. Whenever the user adds or removes a character from the textarea, we want to _update_ to show the remaining limit. + +```mermaid +flowchart TD +A[Step 1: Define the character limit of 200] +Initial[On first load] --> B +Event[When the content changes] --> B +B[Step 2: Access the textarea element] --> C[Step 3: Calculate the number of characters left] --> D[Step 4: Update the interface with the number of characters left] + +classDef hidden display: none; +``` + +Steps 2-4 will be the same, whether we're doing this for the _initial_ load or a subsequent _update_. + This strategy gives us a rough guide for the road ahead. However, as we learn more about this problem, we may need to update our strategy. diff --git a/common-content/en/module/js2/querying/index.md b/common-content/en/module/js2/querying/index.md index 85cce92cb..37607659a 100644 --- a/common-content/en/module/js2/querying/index.md +++ b/common-content/en/module/js2/querying/index.md @@ -18,8 +18,8 @@ Inside the `body` of the html document, we start with the following html: ```html

Character limit

-
@@ -88,9 +88,10 @@ console.log(textarea.value); // evaluates to the value typed by the user {{}} 1. On your local machine, set up a new directory with an `index.html` and `script.js`. -2. Make sure you start with the same static html as the example above. -3. Double check your script file is linked to your html file. +2. Make sure you start with the same static HTML as the example above. +3. Double-check your script file is linked to your html file. 4. Try querying the DOM and accessing various elements like the `textarea` element. +5. Try typing in the `textarea` element, and then accessing its `value` property in Dev Tools. {{}} diff --git a/common-content/en/module/js2/response/index.md b/common-content/en/module/js2/reacting/index.md similarity index 89% rename from common-content/en/module/js2/response/index.md rename to common-content/en/module/js2/reacting/index.md index d5dcf02f1..b6a159cdb 100644 --- a/common-content/en/module/js2/response/index.md +++ b/common-content/en/module/js2/reacting/index.md @@ -48,8 +48,15 @@ In JavaScript, we can pass functions as arguments to other functions. In this ca We can add a log to `updateCharacterLimit` to check it is called every time the `"keyup"` event fires. ```js -const characterLimit = 200; +// We already had the top part of this code before. + const textarea = document.querySelector("textarea"); +const remainingCharacters = textarea.maxLength - textarea.value.length; + +const charactersLeftP = document.querySelector("#character-limit-info"); +charactersLeftP.innerText = `You have ${remainingCharacters} characters remaining`; + +// From here down is new. function updateCharacterLimit() { console.log( @@ -76,7 +83,7 @@ textarea.addEventListener("keyup", updateCharacterLimit); rows="5" maxlength="200" > -

You have 200 characters remaining

+

``` diff --git a/common-content/en/module/js2/refactor/index.md b/common-content/en/module/js2/refactor/index.md new file mode 100644 index 000000000..5242ae291 --- /dev/null +++ b/common-content/en/module/js2/refactor/index.md @@ -0,0 +1,52 @@ ++++ +title = '🧩 Refactor' + +time = 30 +facilitation = false +emoji= '🧩' +[objectives] + 1='Identify and remove duplicated code' +[build] + render = 'never' + list = 'local' + publishResources = false + ++++ + +We have two identical blocks of code: + +```js {linenos=table,linenostart=1 hl_lines=["2-5", "8-10"]} +const textarea = document.querySelector("textarea"); +const remainingCharacters = textarea.maxLength - textarea.value.length; + +const charactersLeftP = document.querySelector("#character-limit-info"); +charactersLeftP.innerText = `You have ${remainingCharacters} characters remaining`; + +function updateCharacterLimit() { + const remainingCharacters = textarea.maxLength - textarea.value.length; + const charactersLeftP = document.querySelector("#character-limit-info"); + charactersLeftP.innerText = `You have ${remainingCharacters} characters remaining`; +} + +textarea.addEventListener("keyup", updateCharacterLimit); +``` + +We know that functions can be used to avoid duplication. We have actually already extracted a function for this functionality for the event handler! Now let's call it from the other place we do the same thing: + +```js +const textarea = document.querySelector("textarea"); + +updateCharacterLimit(); + +function updateCharacterLimit() { + const remainingCharacters = textarea.maxLength - textarea.value.length; + const charactersLeftP = document.querySelector("#character-limit-info"); + charactersLeftP.innerText = `You have ${remainingCharacters} characters remaining`; +} + +textarea.addEventListener("keyup", updateCharacterLimit); +``` + +{{}} +When we think we've completed a goal or sub-goal, we should look at our code and see if we can improve it before we continue. +{{}} diff --git a/common-content/en/module/js2/update/index.md b/common-content/en/module/js2/update/index.md index 011e43989..6425e2346 100644 --- a/common-content/en/module/js2/update/index.md +++ b/common-content/en/module/js2/update/index.md @@ -1,7 +1,7 @@ +++ title = '🏷️ Updating the interface' -time = 45 +time = 25 facilitation = false emoji= '🧩' [objectives] @@ -13,33 +13,65 @@ emoji= '🧩' +++ -We can calculate the remaining characters available every time a user's key is released from the keyboard in the `textarea`. Finally, we must update the `p` element in the user interface with the number of characters remaining. +We know we don't want to always have the number "200" in the text "You have 200 characters remaining". -> Step 5: Update the interface with the number of characters left +We've solved Step 3: Calculate the number of characters left. So we know what value we want to show. -To achieve this goal, we'll need to access the `p` element with id `"character-limit-info"` and then update its text content. As before, we can use `document.querySelector` to access an element in the DOM using an appropriate CSS selector: +All that remains is: +1. To solve Step 4: Update the interface with the number of characters left. +2. To make this happen on page load. +3. To make this also happen when the textarea changes. -```js {linenos=table,linenostart=1, hl_lines=["8-9"] } -const characterLimit = 200; -const textarea = document.querySelector("textarea"); +Instead of writing that text exactly in our HTML, we can use the DOM to set the contents of our `p` tag. + +We can do this by querying the DOM for the element we want to update, and setting its `innerText` property. `innerText` is a property that represents "the text inside the element". -function updateCharacterLimit() { - const charactersLeft = characterLimit - textarea.value.length; - console.log(`${charactersLeft} characters remaining`); +If we change the value of a property in the DOM, it will update the page we're viewing. - const charactersLeftP = document.querySelector("#character-limit-info"); - charactersLeftP.textContent = `You have ${charactersLeft} characters remaining`; -} +Try writing adding this to your `script.js`: -textarea.addEventListener("keyup", updateCharacterLimit); +```js +const limitDisplay = document.querySelector("#character-limit-info"); +limitDisplay.innerText = "You have loaded the page."; ``` -{{}} +Even though our HTML said the paragraph should contain "You have 200 characters remaining", we _replaced_ this text by using the DOM. -{{}} +> Step 4: Update the interface with the number of characters left. -Explain why the code to access the `p` element is written _inside_ the scope of `updateCharacterLimit`. +To achieve this goal, we'll need to access the `p` element with id `"character-limit-info"` and then update its text content. As before, we can use `document.querySelector` to access an element in the DOM using an appropriate CSS selector: -{{}} +```js {linenos=table,linenostart=1, hl_lines=["8-9"] } +const textarea = document.querySelector("textarea"); +const remainingCharacters = textarea.maxLength - textarea.value.length; + +const charactersLeftP = document.querySelector("#character-limit-info"); +charactersLeftP.textContent = `You have ${remainingCharacters} characters remaining`; +``` + +And we can remove the initial text from the `p` tag from our HTML. + +We want to do this because we have another way of setting this. If we wanted to change the text (e.g. to "You **only** have 200 characters remaining"), or change the character limit, we only want to change that one place (in our JavaScript). If we leave the initial value in the HTML, it could get out of date. + +```html {hl_lines=["15"]} + + + + + + Document + + +
+

Character limit

+ + +

+
+ + +``` -{{
}} +We are now _computing_ and _setting_ the character limit info using the DOM on page load. diff --git a/org-cyf-itp/content/data-groups/sprints/3/prep/index.md b/org-cyf-itp/content/data-groups/sprints/3/prep/index.md index 14ddb2f12..391548f9f 100644 --- a/org-cyf-itp/content/data-groups/sprints/3/prep/index.md +++ b/org-cyf-itp/content/data-groups/sprints/3/prep/index.md @@ -6,32 +6,38 @@ menu_level = ['sprint'] weight = 1 [[blocks]] src="module/js2/browser" -name="browser" +name="User interfaces" [[blocks]] src="module/js2/character-limit" -name="character-limit" +name="Implementing a character limit" [[blocks]] name="Step-through-prep workshop" src="https://www.youtube.com/watch?v=0tI34jHLpkY" [[blocks]] src="module/js2/plan" -name="strategy" +name="Breaking down the strategy" [[blocks]] src="module/js2/dom" -name="dom" +name="The DOM" [[blocks]] src="module/js2/querying" -name="querying" +name="Querying the DOM" +[[blocks]] +src="module/js2/calculating" +name="Calculating the remaining characters" +[[blocks]] +src="module/js2/update" +name="Updating the interface" [[blocks]] src="module/js2/events" -name="events" +name="DOM events" [[blocks]] -src="module/js2/response" -name="response" +src="module/js2/reacting" +name="Reacting to events" [[blocks]] src="module/js2/check-progress" name="check progress" [[blocks]] -src="module/js2/update" -name="update element" +src="module/js2/refactor" +name="Refactor" +++