├── .gitignore ├── LICENSE ├── README.md └── challenges ├── index.html ├── part1.js ├── part2.js ├── part3.js ├── part4.js ├── part5.js └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 UIHP-Challenges 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Hard Parts of UI Development - Challenges 2 | 3 | ## Summary 4 | 5 | The purpose of these challenges is to gain an under-the-hood understanding of building user interfaces in the web browser. Each challenge will go hand-in-hand with a section of [The Hard Parts of UI Development Course](https://frontendmasters.com/courses/hard-parts-ui-dev/) by Will Sentance from [Frontend Masters](https://frontendmasters.com/). 6 | 7 | Throughout these challenges, we will cover topics including HTML, JavaScript, & the DOM API in the web browser; one-way data binding and "state-driven" views; the virtual DOM and "composable" UI; and performance optimizations. 8 | 9 | --- 10 | 11 | ## Part 1: Building an Interactive UI 12 | 13 | In UI Engineering we have **2 simple goals**: 14 | 15 | - Display content (data) for users to see. 16 | - Enable our users to interact with the content they see, and then change it. 17 | 18 | 1. First, let's take a look at the `index.html` file. Open it in the browser. 19 | - To do this in your terminal, make sure you have navigated to the correct directory and run the command `open index.html`. This should automatically open the file in your default web browser. 20 | 21 | What do you see? Your tab or window should show the text you see inside the `` in `index.html`, but the page itself should be totally blank. You can also `inspect` the page and view your `html` code in Chrome DevTools. For more info about how to use Chrome DevTools, check out their [docs](https://developer.chrome.com/docs/devtools/). 22 | 23 | 2. Now, uncomment the `<div>` and `<input>` in `index.html` and refresh the page in the browser. (Use `command + /` or `control + /` to comment/uncomment code) When our HTML loads now, it will populate the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) and we can see our input box on the page! Feel free to add some text to your `<div>` and refresh so that you can see it render to the page as well. 24 | 25 | 3. Next, type something into your input box. The pixels on the page show what you have typed - Remember that the underlying DOM data for the `input` node has also updated to reflect your change! 26 | 27 | 4. Uncomment the line containing the `stylesheet` in `index.html` to add some pizazz to our page - remember, when this loads, it will populate the [CSSOM](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model), similar to how our HTML populates the DOM with elements to render. 28 | 29 | Now what? Even if we can change the data on the DOM, we can't do anything with that data or access it directly. We would still need some kind of logic to make something happen in the event that a user types in our input or a button is clicked. Our `html` is only being loaded once and we can't run any code in the DOM. Therefore, we need JavaScript to create, save, and update data. 30 | 31 | 5. Uncomment the `<script>` tag in `index.html`. This script acts as a link between the `html` and the code in the linked JavaScript file. When our `script` loads with our HTML in the browser, the JavaScript engine will start running and allow us to run our JS code in the browser directly (and access everything else we get with the JS runtime, including `memory` to store data). 32 | 33 | --- 34 | 35 | **_Extension Challenge: Document Object_** 36 | _- In our JavaScript runtime, we have access to some very useful APIs, including [document](https://developer.mozilla.org/en-US/docs/Web/API/Document). In `part1.js`, if you `console.log(document)`, you will see an object in the browser dev tools console. This `document` object also has a hidden property that acts as a link to the DOM._ 37 | 38 | --- 39 | 40 | 6. In `part1.js`, declare a variable `'post'` and initialize it to a string that is your name. Congrats - we have data! Now, how can we use it to update the DOM and what we see in our view? 41 | 42 | If you're unfamiliar with DOM manipulation, take a look at the [docs](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) on MDN, and particularly the [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) method (a hidden property on the `document` object), which allows us to query the DOM to select a specific DOM element and create an object in our JavaScript memory with a hidden "link" to access it. That object will have "property-methods" to get or set the data on that DOM element. 43 | 44 | 7. Query the DOM to select the `input` node on the DOM and assign the resulting JS object to a variable called `'jsInput'`. Now do the same for the the `div` node on the DOM and assign the resulting JS object to a variable called `'jsDiv'`. 45 | 46 | 8. Let's use our JS data to update the DOM data. Our `jsDiv` object will have some "getter/setter" property-methods, including [textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent). Set the `jsDiv.textContent` to be the value of our `post` variable. Refresh the page in the browser, and you should see that string as the text in the `div`. 47 | 48 | 9. Now let's update our JavaScript data based on our user interactions. First, edit your variable declaration for `post` at the top of the file to initialize it to an empty string instead. 49 | 50 | Declare a function `'handleInput'` that will "handle" what we want to do with what the user types in the input. Use the `value` getter/setter method available on our `jsInput` object. When the user types into the `input`, reassign `post` to hold that text. 51 | 52 | Set the `textContent` on the `div` to to be the value of `post` here in `handleInput` instead. 53 | 54 | 10. Finally, let's create an `event handler` that will run our `handleInput` function in the event that a user types in `input`. There are several ways to do this, but one is the [`oninput`](https://www.w3schools.com/jsref/event_oninput.asp) property on the `jsInput` object. Us this to set `handleInput` on our input DOM element as a callback. 55 | 56 | We should now have a full User Interface (UI) which addresses our two main goals: 57 | 58 | - Display content (data inside computer/from internet) as the 'view' for users to see, and 59 | - Enable the user to interact (tap, click, etc) with the 'view' they see and change it (by changing the udnerlying data & updating the view). 60 | 61 | --- 62 | 63 | ## Part 2: One-way Data Binding 64 | 65 | Now that we have a simple application our users can interact with, how can we make our UI more sophisticated? One thing we can do is implement one-way data binding, a popular paradigm for tackling the essential engineering challenge of keeping the 'view' consistent with (and dependent on) our underlying data. 66 | 67 | 1. In `index.html`, edit the script tag to use `part2.js` as the `src` file. 68 | 69 | 2. What if we wanted to add some placeholder text to the input box? In your JS file, set the `value` of the `input` box to be the string 'What's on your mind?' and refresh the browser. 70 | 71 | Click into the input and type something. Notice that the user needs to manually delete the placeholder text that is in the input box every time they want to type in a new input. 72 | 73 | 3. Let's add a click handler `'handleClick'` for the input box which sets the `value` inside the input box to an empty string when the user clicks in it to type. HINT: Make sure to add `handleClick` as a callback to the input element. 74 | 75 | Now we're changing our 'view' based on several different possible user interactions. How can we make these changes more predictable? Let's restrict every change to view to be via: 76 | 77 | - an update of 'data' 78 | - a run of a single `dataToView` convertor function. 79 | 80 | 4. To do this, let's create a function `'dataToView'`. It should: 81 | 82 | - Update the value in our input box on the DOM to be whatever `post` currently is. HINT: `post` should now start off as undefined, and if it is undefined when `dataToView` is invoked, we should populate the value in the input box to be the string containing 'What's on your mind?'. This also means `dataToView` needs to be invoked when the page is first loaded. 83 | 84 | - Update the content of the div on the DOM to be our current data. 85 | 86 | 5. Now that the `dataToView` function uses our JS data to update the DOM content, we need to make sure it is invoked after our data changes. What adjustments can we make to our event handlers so that they only make changes to the underlying data, and what should happen as soon as a change is made? 87 | 88 | 6. While it may not be efficient, we can have our `dataToView` run so often that any change to data will instantly propagate using a [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) function at a rate that is close to the browser refresh rate. 89 | 90 | --- 91 | 92 | **_Extension Challenge: Submit Button_** <br/> 93 | _Add a submit button that will create and save a new post. How can you implement this kind of functionality in your application? In addition, set up some logic for creating divs that hold previous 'posts' so that the user is able to see all their previous posts._ 94 | 95 | --- 96 | 97 | ## Part 3: The Virtual DOM 98 | 99 | In our first two UI Hard Parts challenges, we displayed content and let our users interact with it to change the underlying data, and implemented one-way data binding to ensure our view is always dependent on and consistent with that data. 100 | 101 | In `part3.js`, we are describing key parts of the UI in `dataToView`: the contents (data) and how to display it. What if we described the elements as well? This would make our funnction a complete description of the data _and_ view. 102 | 103 | 1. In `index.html`, edit the `script` tag to use `part3.js` as the `src` file. Then comment out or delete the `input` and `div` elements. 104 | 105 | Instead, let's create our DOM elements with JavaScript. A `UI Component` is a function that fully creates elements and relates their data to the view. 106 | 107 | 2. In `part3.js`, uncomment the function `'component'` and build it out. You will see it has already been started for you. 108 | 109 | - The first and last lines of code in this function are there to make sure that once your user has clicked in the input, they will stay "clicked" and able to type as `component` is repeatedly called (hint!) to auto-update the DOM. 110 | 111 | This function will combine all of our previous functionality into one: 112 | 113 | - [Creating](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) DOM elements (instead of selecting them). 114 | - Feel free to keep the `jsInput` and `jsDiv` variable declarations at the top of the file, but unitialized - you will reassign the values to be the new objects you create. 115 | - Setting their contents based on our JS data. 116 | - Once this happens inside `component`, we should no longer need our `dataToView` function. 117 | - Creating and attaching event handlers to them. 118 | - Displaying our new DOM elements in the view. 119 | 120 | In order to achieve this last step, we need to attach or `append` them to the body of the DOM. There a a couple of ways to do this, but to make sure we are replacing our nodes with an updated version when our data changes (instead of accidentally attaching multiple copies of our input or div), use [replaceChildren](https://developer.mozilla.org/en-US/docs/Web/API/Document/replaceChildren). 121 | 122 | Now, our view should look the same as it did before, but our code should consist of 3 parts - our declared variables at the top of the file, the `component` function (where all of our functionality will be now), and `setInterval`. 123 | 124 | However, our code is still fairly imperative - just glancing at it, it's hard to tell what our view will look like. The more "visual" our code is (like HTML), the easier it is for us to work with as developers. 125 | 126 | For example - imagine we wanted the text content of a div element to be the string "I live in (user's location)!". We can use the `concat` method to build out our string piece by piece: 127 | 128 | ```javascript 129 | let userLocation = 'LA'; 130 | 131 | let textToDisplay = 'I live in '; 132 | textToDisplay = textToDisplay.concat(userLocation); 133 | textToDisplay = textToDisplay.concat('!'); 134 | ``` 135 | 136 | However, this is not very visual or declarative. So instead, we can use a `template literal` with `string interpolation`: 137 | 138 | ```javascript 139 | textToDisplay = `I live in ${userLocation}!`; 140 | ``` 141 | 142 | As we can see, this mirrors what our visual/graphic output will be. Could we do something similar with our main code creating visual elements? 143 | 144 | Let's start with a "unit of code" representing each piece of our view. 145 | 146 | ```javascript 147 | const divInfo = ['div', `Hi, ${myName}!`]; 148 | ``` 149 | 150 | In this example, our `divInfo` is an array with the details of a DOM element - just by looking at our code, we can tell what it will look like (the type (div) and the text that will display inside). 151 | 152 | 3. In `part3.js`, write a function `'convert'` that will take in a `node` (an array of details like our 'divInfo' example above). This function should use that array to create a new DOM element and set its content, and return its linked JavaScript object. 153 | 154 | With this function, we can now produce DOM elements from any list of info that visually mirrors what we will see in our view. 155 | 156 | Next, let's create our `virtual DOM` - blocks of code (or a _list_) representing each piece of our view. 157 | 158 | 4. Declare a global variable `'vDOM'` but do not initialize it. 159 | 160 | 5. Write a function `'createVDOM'` that returns a list (array) containing the following two sub-arrays: 161 | 162 | ```javascript 163 | [ 164 | 'input', 165 | myName, 166 | function handle() { 167 | myName = jsInput.value; 168 | }, 169 | ][('div', `Hello, ${myName}!`)]; 170 | ``` 171 | 172 | - Notice that in each sub-array, index `[0]` is the type of DOM element we want to create, index `[1]` has details of what we want that element to contain or display, and index `[2]` is an event handler callback function. 173 | 174 | 6. Edit the `convert` function to make sure it will properly convert each sub-array in our `vDOM` into a DOM element, accounting for the different properties on different element types, as well as setting any event handlers. 175 | 176 | 7. Declare a function `'updateDOM'`. This function will: 177 | - Update our `vDOM` variable to be the returned result of invoking `createDOM()`. 178 | - Use the `convert` function with our new `vDOM` details to update `jsInput` and `jsDiv` with a new DOM element (linked through a JS object). 179 | - Replace any children on the body of the DOM with our new elements. 180 | - Re-set the `focus` to be on the input if it has been clicked on (feel free to copy this code from the `component` function where it was given to you). 181 | 182 | Now, all of `component`'s functionality should be replaced by the `convert` and `updateDOM` functions. 183 | 184 | 8. Since `updateDOM` is now the function that updates the DOM with our current data, make sure that it is being called regularly so any data changes will reflect on the DOM. 185 | 186 | --- 187 | 188 | **_Extension Challenge: Adding Nested Elements to the Virtual DOM_** <br/> 189 | _What if we wanted more control over placement - what if we had nested elements? Add a few nested elements to the `createVDOM` function return. How might you adjust your `convert` function so that it can handle nested elements in the vDOM using semi-visual coding?_ 190 | 191 | --- 192 | 193 | ## Part 4: Flexible DOM Composition 194 | 195 | So far, we have created a "virtual" DOM in JavaScript to be a visual representation of our actual DOM elements, and combined it with one-way data binding. This means our JS data is our "single source of truth" and our `vDOM` is always consistent with the actual DOM. 196 | 197 | Right now, our `vDOM` is a list of 2 arrays with their element details - but in the "real world" we'll likely have hundreds (or thousands!) of DOM elements to create. We need tools to deal with lists - in this case, we are using an `array` to create our list. 198 | 199 | 1. In `index.html`, edit the `script` tag to use `part4.js` as the `src` file. 200 | 201 | In `part4.js`, we have a `vDOM` variable (a LIST of subarrays with element details). We also have the same `convert` function we used before to create a DOM element and corresponding linked JavaScript object with the info in one of our vDOM subarrays. Previously, we manually called `convert` on vDOM[0] and vDOM[1]. However, if we have hundreds or thousands of DOM elements to create, this approach is not very efficient or flexible. 202 | 203 | 2. How can we make sure we always run our `convert` function on each subarray of our `vDOM`, no matter how many there are or if the number of elements in our vDOM changes? (HINT: since we are working with an `array`, we can use the `map` [method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)). 204 | 205 | 3. Store the resulting _list_ of "converted" JS objects with linked DOM elements in a new variable called `'elems'`. 206 | 207 | 4. Now our new elements exist on the DOM - what do we need to do to render them to the page? How can we make sure each of our `elems` is appended to the DOM, no matter how many there are? 208 | 209 | 5. Add 1-2 extra `div` subarrays to the `vDOM` and refresh the page - we can now flexibly convert however many elements we have in our vDOM! 210 | 211 | With our new "element-flexible" code, we can "compose" our DOM elements. 212 | 213 | 6. Comment out all of the code at the top of the file, and uncomment all the code below the marked line (this should look familiar - the same `createVDOM`, `updateDOM`, and `convert` functions we used before). 214 | 215 | 7. Edit the `updateDOM` function to flexibly handle an unknown amount of elements in the vDOM. Make sure that all of the `elems` we create are appended to the DOM to replace the previous child elements. 216 | 217 | 8. Add another element (or a few) to the vDOM in `createVDOM` - again, no other code should have to change and they should all render properly. 218 | 219 | 9. For even more flexibility in our code, we can edit our `input` event handler function to use the [event API](https://developer.mozilla.org/en-US/docs/Web/API/Event) and [target property](https://developer.mozilla.org/en-US/docs/Web/API/Event/target). 220 | 221 | We're getting semi-visual coding - yay! 222 | 223 | --- 224 | 225 | **_Extension Challenges: Additional Functionality_** 226 | <br/> 227 | 228 | **_Directives:_** <br/> 229 | _Another way that we can give our elements more functionality is by creating functions that take in our element and "decorate it" with added functionality before returning it. In this case, each element on the page has a chance to "do" something in the user's eyes. In reality, that "doing" is happening in JavaScript (e.g. checking a conditional, a loop, etc) and then updating the view (DOM). Try making your elements store some kind of functionality._ 230 | 231 | **_Functional Components:_** <br/> 232 | _We can also improve our VDOM elements to include additional functionality by creating our elements with a function. This function returns the element out, but before it does, it can run JavaScript code to determine exactly what will be returned. How would you refactor your elements to make them functional in this way? Popular UI frameworks such as React embrace this style of engineering, so if you haven't explored functional components yet, now's your chance!_ 233 | 234 | --- 235 | 236 | ## Part 5: Hooks & Diffing 237 | 238 | Now we've learned to use a "Virtual" DOM for semi-visual coding and we've also created 'element-flexible' code to 'compose' our DOM elements. However, completely rebuilding the entire DOM from scratch every 15 milliseconds makes for terrible performance - as is, our lovely vDOM is quite unusable. We need to add efficiency. 239 | 240 | 1. In `index.html`, edit the `script` tag to use `part5.js` as the `src` file. 241 | 242 | 2. First, let's get rid of the `setInterval` to avoid repeated function calls to `updateDOM`. 243 | - However, we still need to make sure we create our vDOM and populate the DOM with elements on our initial load/render. 244 | 245 | We _could_ call `updateDOM` on every data change (like we did previously inside our event handlers) - but that only works if we _actually_ remember to invoke it everywhere we'd need to, which is unlikely (especially if we have thousands of user actions to handle) - so then our vDOM isn't "real" or guaranteed to accurately reflect our data. 246 | 247 | What if, instead of directly updating our data, we run a function to do so? 248 | 249 | 3. Initialize a function `'updateName'` which takes in a `value`. This function should reassign `myName` to be the passed-in value. 250 | 251 | - `updateName` should _also_ make sure the DOM is updated after we update our data. 252 | 253 | 4. Make sure any part of your code that previously updated `myName` directly is edited to use `updateName` instead. 254 | 255 | If we _only_ ever update `myName` with our new `updateName` function - instead of updating `myName` directly - we can be sure that our DOM is always updated after a data change. 256 | 257 | - We would likely "lock down" `myName` so that it cannot be accessed directly (or updated from outside of our updater function). 258 | 259 | What if we had more data to manage than `myName`? (Realistically, we would - _much_ more.) Our `updateName` function is great - but can we refactor it so that it works for _any_ piece of data we might have that our view is dependent on? 260 | 261 | 5. First, declare a new variable `'data'` at the top of our file, and initialize it to an object. Instead of declaring `myName` separately, nest it inside `data` as a key-value pair. That way, our data "store" is more flexible and we can add more things to it as needed. 262 | 263 | 6. Since we want to make the `updateName` function work with any data, edit it to be called `'updateData'` instead. Have it take in a `'label'` in addition to the `value`. 264 | 265 | 7. `updateData` should do two things: 266 | 267 | - Update the `value` on our `data` object that is stored under the passed in `label` (or create it if it does not already exist). 268 | - Update the DOM after our data has been changed. 269 | 270 | 8. Since we have just implemented changes to the structure of our `data`, as well as our `updateData` function, make sure that any place in our code that references our data or updates it is adjusted accordingly. 271 | 272 | Now, `updateData` works to update any piece of data - or _state_ - and so allows us to just "hook" into it. We have a "state hook"! 273 | 274 | --- 275 | 276 | **_Extension Challenge: requestAnimationFrame()_** <br/> 277 | _Implement [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) rather than `updateDom` directly on data change - so that it never prioritizes over animations (CSS etc)._ 278 | 279 | --- 280 | 281 | With our new `updateData` **hook**, we have eliminated unnecessary repeated calls to `updateDOM` and make sure we are only updating the DOM if our data has changed. However, we are still recreating the entire DOM from scratch every time - even when some of our elements will be rendered exactly the same. 282 | 283 | To solve this problem, we can write an 'algorithm' (a series of "smart instructions"), to check what elements actually **differ** on our updated DOM as compared the the previous DOM - and only change the DOM elements that need to be updated. 284 | 285 | So, let's write a diffing algorithm! 286 | 287 | 9. You're provided with the first half of this function `'findDiff'` - go ahead and uncomment it. As you can see, it takes in two parameters, `prevVDOM` and `currentVDOM`. Declare `'prevVDOM'` globally at the top of the file (but do not initiate it). 288 | 289 | - Inside of `findDiff`, we're iterating through each element of the `currentVDOM` array with a for loop. 290 | - Then, we are using `JSON.stringify` to check if each element of our `currentVDOM` matches the corresponding element of `prevVDOM` or if it has changed. Feel free to read the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), but don't worry too much about this method right now -- the point of using it here is to compare for equal values, not to check if two objects/arrays are referencing the same place in memory. 291 | 292 | 10. If a certain element of our `currentVDOM` has changed (when our data has updated) and does not match what is on our `prevVDOM`, `findDiff` should update (or _reconcile_) the actual `DOM` element related to that vDOM element. 293 | 294 | - Where have we stored the JavaScript objects we have created with hidden links that allow us to interact with their corresponding nodes on the DOM? 295 | - How can you make sure you **set** the correct content on each DOM element based on what's on our new/current vDOM? 296 | 297 | Finally, we want to put our new diffing algorithm to good use! In order to implement this, we need to adjust how we update the DOM in order to avoid a full repaint of our view every time. 298 | 299 | 11. In `updateDOM` (which will be invoked every time we update our data, via our "state hook"), a couple of things need to happen: 300 | - If `elems` (where we store our JS objects that correspond/link to the DOM elements we create) is undefined, we need to create those elements with our vDOM, and then attach them to the DOM. 301 | - If we already have `elems` on the DOM, we want to reassign `prevVDOM` to be a new array, and then _spread_ (hint hint) all of the elements of our existing vDOM into it. Then, we want to update our `vDOM` with our current data, and use our **diffing** algorithm to only update the DOM data we need to. 302 | 303 | --- 304 | 305 | Congratulations! Starting with breaking down the most granular under-the-hood operations in our UI Hard Parts journey, we have built up to understanding a groundbreaking approach to UI: 306 | 307 | - Displayed **data/content** that our users can **interact with** (our two goals!). 308 | - Created **single source of truth** for our data (with one-way data binding). 309 | - Implemented "semi-visual" code with our **virtual/visual DOM** that allows us to flexibly compose our UI. 310 | - Harnessed techniques for **efficiency** like **state hooks**, **diffing**, and **reconciliation**. 311 | -------------------------------------------------------------------------------- /challenges/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 | <title>The Hard Parts of UI Development - Challenges 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /challenges/part1.js: -------------------------------------------------------------------------------- 1 | /** Write your code below */ -------------------------------------------------------------------------------- /challenges/part2.js: -------------------------------------------------------------------------------- 1 | let post = ''; 2 | 3 | const jsInput = document.querySelector('input'); 4 | const jsDiv = document.querySelector('div'); 5 | 6 | function handleInput() { 7 | post = jsInput.value; 8 | jsDiv.textContent = post; 9 | } 10 | 11 | jsInput.oninput = handleInput; 12 | -------------------------------------------------------------------------------- /challenges/part3.js: -------------------------------------------------------------------------------- 1 | let myName = ''; 2 | let isFocus = false; 3 | 4 | let jsInput = document.querySelector('input'); 5 | let jsDiv = document.querySelector('div'); 6 | 7 | function dataToView() { 8 | jsInput.value = myName; 9 | jsDiv.textContent = myName; 10 | } 11 | 12 | function handleInput() { 13 | myName = jsInput.value; 14 | } 15 | 16 | jsInput.oninput = handleInput; 17 | 18 | // function component() { 19 | // document.activeElement === jsInput ? (isFocus = true) : (isFocus = false); 20 | 21 | // //your code here 22 | 23 | // if (isFocus) jsInput.focus(); 24 | // } 25 | 26 | setInterval(dataToView, 15); 27 | -------------------------------------------------------------------------------- /challenges/part4.js: -------------------------------------------------------------------------------- 1 | let myName = ""; 2 | 3 | const vDOM = [ 4 | [ 5 | "input", 6 | myName, 7 | function handle() { 8 | myName = jsInput.value; 9 | }, 10 | ], 11 | ["div", `Hello, ${myName}!`], 12 | ]; 13 | 14 | function convert(node) { 15 | const element = document.createElement(node[0]); 16 | element.textContent = node[1]; 17 | element.value = node[1]; 18 | element.oninput = node[2]; 19 | return element; 20 | } 21 | 22 | /** Step @todo */ 23 | /* uncomment the code below this line, and comment out the code above*/ 24 | 25 | // let isFocus = false 26 | 27 | // let myName = ''; 28 | // let jsInput; 29 | // let jsDiv; 30 | // let vDOM; 31 | 32 | // function createVDOM() { 33 | // return [ 34 | // [ 35 | // 'input', 36 | // myName, 37 | // function handle() { 38 | // myName = jsInput.value; 39 | // }, 40 | // ], 41 | // ['div', `Hello, ${myName}!`], 42 | // ]; 43 | // } 44 | 45 | // function updateDOM() { 46 | // document.activeElement === jsInput ? (isFocus = true) : (isFocus = false); 47 | // vDOM = createVDOM(); 48 | // jsInput = convert(vDOM[0]); 49 | // jsDiv = convert(vDOM[1]); 50 | // document.body.replaceChildren(jsInput, jsDiv); 51 | // if(isFocus) jsInput.focus() 52 | // } 53 | 54 | // function convert(node) { 55 | // const element = document.createElement(node[0]); 56 | // element.textContent = node[1]; 57 | // element.value = node[1]; 58 | // element.oninput = node[2]; 59 | // return element; 60 | // } 61 | 62 | // setInterval(updateDOM, 15); 63 | -------------------------------------------------------------------------------- /challenges/part5.js: -------------------------------------------------------------------------------- 1 | let myName = ""; 2 | let vDOM; 3 | let elems; 4 | 5 | function createVDOM() { 6 | return [ 7 | [ 8 | "input", 9 | myName, 10 | function handle(e) { 11 | myName = e.target.value; 12 | }, 13 | ], 14 | ["div", `Hello, ${myName}!`], 15 | ["div", `Great job, Jonathan!`], 16 | ["div", `Great job, Alexa!`], 17 | ["div", `Great job, Emilia!`], 18 | ]; 19 | } 20 | 21 | function updateDOM() { 22 | if (vDOM) 23 | document.activeElement == document.querySelector("input") 24 | ? (isFocus = true) 25 | : (isFocus = false); // keep this code 26 | vDOM = createVDOM(); 27 | elems = vDOM.map(convert); 28 | document.body.replaceChildren(...elems); 29 | if (isFocus) elems[0].focus(); //keep this code 30 | } 31 | 32 | function convert(node) { 33 | const element = document.createElement(node[0]); 34 | element.textContent = node[1]; 35 | element.value = node[1]; 36 | element.oninput = node[2]; 37 | return element; 38 | } 39 | 40 | // function findDiff(prevVDOM, currentVDOM) { 41 | // for (let i = 0; i < currentVDOM.length; i++) { 42 | // if(JSON.stringify(prevVDOM[i]) !== JSON.stringify(currentVDOM[i])){ 43 | // // change the actual DOM element related to that vDOM element! 44 | // } 45 | // } 46 | // } 47 | 48 | setInterval(updateDOM, 15); 49 | -------------------------------------------------------------------------------- /challenges/styles.css: -------------------------------------------------------------------------------- 1 | /*Yes...these styles are awful!*/ 2 | 3 | body { 4 | background-color: cornflowerblue; 5 | color: linen; 6 | text-align: center; 7 | } --------------------------------------------------------------------------------