From 216c3ea7d66e147cde1b708758e294f92d3710b0 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Sun, 17 Nov 2019 20:32:43 +0000 Subject: [PATCH 1/6] created two examples --- 05 The DOM.md | 29 +++++++++ .../todo/acceptance tests/ui.test.js | 6 +- exercises/05_client/api_client/index.html | 23 +++++++ exercises/05_client/api_client/script.js | 65 +++++++++++++++++++ exercises/05_client/api_client/style.css | 11 ++++ exercises/05_client/todo/index.html | 22 +++++++ exercises/05_client/todo/script.js | 39 +++++++++++ exercises/05_client/todo/style.css | 4 ++ exercises/05_mqtt/chat/Messages.js | 17 +++-- 9 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 05 The DOM.md create mode 100644 exercises/05_client/api_client/index.html create mode 100644 exercises/05_client/api_client/script.js create mode 100644 exercises/05_client/api_client/style.css create mode 100644 exercises/05_client/todo/index.html create mode 100644 exercises/05_client/todo/script.js create mode 100644 exercises/05_client/todo/style.css diff --git a/05 The DOM.md b/05 The DOM.md new file mode 100644 index 0000000..d4d6838 --- /dev/null +++ b/05 The DOM.md @@ -0,0 +1,29 @@ + +# The Document Object Model + +In all the previous labs you have been writing JavaScript code to run on the server however the language was originally written to run in the web browser! In this lab you will be exploring how the JavaScript labguage can be used to directly interact with the HTML content in the web browser. + +## 1 The DOM + +## 2 Making API Calls + +In this section you will learn how to retrieve data from an API, specifically we will be covering: + +1. How to make a GET call to a RESTful API. +2. Building a dropdown list using dynamic data. +3. Building a table using dynamic data. + +### 2.1 Test Your Understanding + +1. Modify the HTML page to add: + 1. A second dropdown list with a name of `converted`. This should contain the same currency list as the `base` dropdown. + 2. A textbox that only accepts numerical data, call this `amount`. +2. Modify the event listener at the top of the script so that changes to either of the dropdown lists triggers the `getRates()` function. +3. Currently the `getRates()` function checks to see that the `base` selection has been made before it generates the table of rates. Modify this function so that it only generates the table if: + 1. The use makes a selection from the second dropdown. + 2. The user has entered a numerical value in the `amount` box. +4. If the user has done the above replace the table with a `

` element that displays the conversion rate between the two selected currencies: + 1. Use this example `https://api.exchangeratesapi.io/latest?base=GBP&symbols=USD` as a guide. +5. Finally multiply this rate by the amount from the textbox and display the result instead. + +## 3 Handling Data Persistence diff --git a/exercises/03_acceptance/todo/acceptance tests/ui.test.js b/exercises/03_acceptance/todo/acceptance tests/ui.test.js index a794b45..56eac27 100644 --- a/exercises/03_acceptance/todo/acceptance tests/ui.test.js +++ b/exercises/03_acceptance/todo/acceptance tests/ui.test.js @@ -68,15 +68,15 @@ describe('todo list', () => { const arr = Array.from(dom) return arr.map(td => td.innerText) }) - const numRows = items.reduce( (acc, val) => (val === 'bread' ? acc += 1 : acc), 0) + const numRows = items.reduce( (acc, val) => val === 'bread' ? acc += 1 : acc, 0) console.log(`numRows: ${numRows}`) // returns the number of rows containing the given string const items2 = await page.evaluate(() => Array .from(document.querySelectorAll('table tr td:first-child')) - .map(td => td.innerHTML) - .reduce( (acc, val) => (val === 'bread' ? acc += 1 : acc), 0) + .map(td => td.innerHTML) + .reduce( (acc, val) => val === 'bread' ? acc += 1 : acc, 0) ) expect(items.length).toBe(1) diff --git a/exercises/05_client/api_client/index.html b/exercises/05_client/api_client/index.html new file mode 100644 index 0000000..06278d3 --- /dev/null +++ b/exercises/05_client/api_client/index.html @@ -0,0 +1,23 @@ + + + + + + + + Currency Converter + + + + + + +

Currency Converter

+

+ Base currency: + +
+ + diff --git a/exercises/05_client/api_client/script.js b/exercises/05_client/api_client/script.js new file mode 100644 index 0000000..c237137 --- /dev/null +++ b/exercises/05_client/api_client/script.js @@ -0,0 +1,65 @@ + +'use strict' + +const baseURL = 'https://api.exchangeratesapi.io/latest' + +document.addEventListener('DOMContentLoaded', async() => { + console.log('page loaded') + await createDropdown() + document.querySelector('select[name="base"]').onchange = getRates +}) + +// populates the currency dropdown with a list of currency codes. +async function createDropdown() { + const data = await makeRequest(`${baseURL}`) + data.rates.EUR = 1.0 + const select = document.querySelector('select[name="base"]') + Object.keys(data.rates).sort().forEach( val => { + const node = document.createElement('option') + node.appendChild(document.createTextNode(val)) + select.appendChild(node) + }) +} + +// retrieves the value from the dropdown options list +async function getRates() { + const e = document.querySelector('select[name="base"]') + const base = e.options[e.selectedIndex].value + const table = document.querySelector('table') + table.innerHTML = '' + if(base) { + document.querySelector('h2').innerHTML = `Base Currency: ${base}` + const data = await makeRequest(`${baseURL}?base=${base}`) + buildTable(data.rates) + } +} + +// creates a new html table with the data from the parameter +function buildTable(rates) { + const table = document.querySelector('table') + Object.keys(rates).sort().forEach( val => { + if(rates[val] !== 1) { + const row = table.insertRow(-1) + row.insertCell(0).innerHTML = val + row.insertCell(1).innerHTML = rates[val] + } + }) +} + +// this function converts the standard XMLHTTPRequest to a promise +const makeRequest = (url, method = 'GET') => new Promise( (resolve, reject) => { + const ok = 2 + const percent = 100 + const xhr = new XMLHttpRequest() + xhr.open(method, url) + xhr.onload = function() { + if ( Math.floor(this.status/percent) !== ok) { // if response code does not start with 2 + return reject({status: this.status, statusText: xhr.statusText }) + } + return resolve(JSON.parse(xhr.response)) + } + xhr.onerror = function() { + return reject({ status: this.status, statusText: xhr.statusText }) + } + xhr.send() +}) diff --git a/exercises/05_client/api_client/style.css b/exercises/05_client/api_client/style.css new file mode 100644 index 0000000..2af0a2d --- /dev/null +++ b/exercises/05_client/api_client/style.css @@ -0,0 +1,11 @@ + +body { + font-family: verdana; +} + +tr { + background-color: #fff; +} +tr:hover { + background-color: #f3f8aa; +} \ No newline at end of file diff --git a/exercises/05_client/todo/index.html b/exercises/05_client/todo/index.html new file mode 100644 index 0000000..fc444d7 --- /dev/null +++ b/exercises/05_client/todo/index.html @@ -0,0 +1,22 @@ + + + + + + + + ToDo List + + + + + + +

ToDo List

+
+ + +
    +
    + + diff --git a/exercises/05_client/todo/script.js b/exercises/05_client/todo/script.js new file mode 100644 index 0000000..566eadc --- /dev/null +++ b/exercises/05_client/todo/script.js @@ -0,0 +1,39 @@ + +'use strict' + +document.addEventListener('DOMContentLoaded', () => { + console.log('page loaded') + // document.querySelectorAll('input').forEach(elem => elem.addEventListener('keyup', addText)) + document.querySelectorAll('input').forEach( elem => elem.onkeyup = addText ) + document.querySelector('form').onsubmit = addItem + document.querySelector('ol').onclick = deleteRow +}) + +function addText(e) { + const enterKey = 13 + console.log('keyup') + console.log(e.target.name) + if(e.which === enterKey) addItem(e) +} + +function addItem() { + console.log('addItem') + const itemField = document.querySelector('input[name="item"]') + const item = itemField.value + itemField.value = '' + itemField.focus() + if(item.length) addNode(item) + return false // prevents the form from submitting +} + +function addNode(item) { + const node = document.createElement('LI') // Create a
  1. node + const textnode = document.createTextNode(item) // Create a text node + node.appendChild(textnode) // Append the text to
  2. + document.querySelector('ol').appendChild(node) +} + +function deleteRow(e) { + console.log('delete row') + this.removeChild(e.target) +} diff --git a/exercises/05_client/todo/style.css b/exercises/05_client/todo/style.css new file mode 100644 index 0000000..4839ea5 --- /dev/null +++ b/exercises/05_client/todo/style.css @@ -0,0 +1,4 @@ + +body { + font-family: verdana; +} diff --git a/exercises/05_mqtt/chat/Messages.js b/exercises/05_mqtt/chat/Messages.js index 8457de4..fdf1bde 100644 --- a/exercises/05_mqtt/chat/Messages.js +++ b/exercises/05_mqtt/chat/Messages.js @@ -8,12 +8,21 @@ module.exports = class Messages { return this } extractData() { - + // TODO } add() { - + // TODO + } + get html() { + // TODO + } + get count() { + // TODO + } + get all() { + // TODO } - getAll() { - + get clear() { + // TODO } } From 5df89990e6e1d4aaa50f80845f79f9b6da89b562 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Sun, 17 Nov 2019 22:16:51 +0000 Subject: [PATCH 2/6] created simple dom tasks --- 05 The DOM.md | 34 +++++++- exercises/05_client/todo/index.html | 3 +- exercises/05_client/todo/script.js | 20 +++-- exercises/05_client/todo/style.css | 13 +++ .../solutions/05_client/api_client/index.html | 30 +++++++ .../solutions/05_client/api_client/script.js | 79 +++++++++++++++++++ .../solutions/05_client/api_client/style.css | 11 +++ 7 files changed, 178 insertions(+), 12 deletions(-) create mode 100644 exercises/solutions/05_client/api_client/index.html create mode 100644 exercises/solutions/05_client/api_client/script.js create mode 100644 exercises/solutions/05_client/api_client/style.css diff --git a/05 The DOM.md b/05 The DOM.md index d4d6838..8d8e9c3 100644 --- a/05 The DOM.md +++ b/05 The DOM.md @@ -5,7 +5,25 @@ In all the previous labs you have been writing JavaScript code to run on the ser ## 1 The DOM -## 2 Making API Calls +### 1.1 Test Your Understanding + +1. When the user tries to delete an item, use the `alert()` function to ask if they are sure. +2. When a row is deleted, bring the focus back to the input box. +3. Use the `e.target.innerHTML` property to prevent the row being deleted unless the user clicks on the `delete` link. +4. Add a second textbox called `qty`, this will hold the number of items being added. +5. Add a second column to the table to display the quantity of each item. +6. Prevent items being added if they are already in the list. +7. When a duplicate item is added increase the qty of the item already in the list. + +## 2 Form Validation + +Handling form validation + +Revealing sections of the form + +Preventing form submission + +## 3 Making API Calls In this section you will learn how to retrieve data from an API, specifically we will be covering: @@ -13,7 +31,7 @@ In this section you will learn how to retrieve data from an API, specifically we 2. Building a dropdown list using dynamic data. 3. Building a table using dynamic data. -### 2.1 Test Your Understanding +### 3.1 Test Your Understanding 1. Modify the HTML page to add: 1. A second dropdown list with a name of `converted`. This should contain the same currency list as the `base` dropdown. @@ -21,9 +39,17 @@ In this section you will learn how to retrieve data from an API, specifically we 2. Modify the event listener at the top of the script so that changes to either of the dropdown lists triggers the `getRates()` function. 3. Currently the `getRates()` function checks to see that the `base` selection has been made before it generates the table of rates. Modify this function so that it only generates the table if: 1. The use makes a selection from the second dropdown. - 2. The user has entered a numerical value in the `amount` box. + 2. The user has entered a numerical value in the `amount` box (don't forget to convert the amount entered into a number and add an `onkeyup` event handler!). 4. If the user has done the above replace the table with a `

    ` element that displays the conversion rate between the two selected currencies: 1. Use this example `https://api.exchangeratesapi.io/latest?base=GBP&symbols=USD` as a guide. 5. Finally multiply this rate by the amount from the textbox and display the result instead. + 1. Round the value down to the nearest whole number. + 2. Display the currency code as well. + +## 4 Handling Data Persistence + +Using localstorage (and sqlite)? + +### 4.1 Test Your Understanding -## 3 Handling Data Persistence +xxx diff --git a/exercises/05_client/todo/index.html b/exercises/05_client/todo/index.html index fc444d7..2f74f9f 100644 --- a/exercises/05_client/todo/index.html +++ b/exercises/05_client/todo/index.html @@ -15,7 +15,8 @@

    ToDo List

    - + +
      diff --git a/exercises/05_client/todo/script.js b/exercises/05_client/todo/script.js index 566eadc..b3afa28 100644 --- a/exercises/05_client/todo/script.js +++ b/exercises/05_client/todo/script.js @@ -3,10 +3,11 @@ document.addEventListener('DOMContentLoaded', () => { console.log('page loaded') - // document.querySelectorAll('input').forEach(elem => elem.addEventListener('keyup', addText)) + // document.querySelectorAll('td').onclick = deleteRow document.querySelectorAll('input').forEach( elem => elem.onkeyup = addText ) document.querySelector('form').onsubmit = addItem - document.querySelector('ol').onclick = deleteRow + // document.querySelector('table').onclick = deleteRow + document.querySelector('table').onclick = deleteRow }) function addText(e) { @@ -27,13 +28,18 @@ function addItem() { } function addNode(item) { - const node = document.createElement('LI') // Create a
    1. node - const textnode = document.createTextNode(item) // Create a text node - node.appendChild(textnode) // Append the text to
    2. - document.querySelector('ol').appendChild(node) + // const node = document.createElement('li') // Create a
    3. node + // const textnode = document.createTextNode(item) // Create a text node + // node.appendChild(textnode) // Append the text to
    4. + // document.querySelector('ol').appendChild(node) + const table = document.querySelector('table') + const row = table.insertRow(-1) + row.insertCell(0).innerHTML = item + row.insertCell(1).innerHTML = 'delete' } function deleteRow(e) { console.log('delete row') - this.removeChild(e.target) + const index = e.target.parentElement.rowIndex + document.querySelector('table').deleteRow(index) } diff --git a/exercises/05_client/todo/style.css b/exercises/05_client/todo/style.css index 4839ea5..bfe701f 100644 --- a/exercises/05_client/todo/style.css +++ b/exercises/05_client/todo/style.css @@ -2,3 +2,16 @@ body { font-family: verdana; } + +table { + margin: 20px; + border-spacing: 0; +} + +td { + border: 1px solid grey; + margin: 0; + padding: 5px; +} + + diff --git a/exercises/solutions/05_client/api_client/index.html b/exercises/solutions/05_client/api_client/index.html new file mode 100644 index 0000000..a916faa --- /dev/null +++ b/exercises/solutions/05_client/api_client/index.html @@ -0,0 +1,30 @@ + + + + + + + + Currency Converter + + + + + + +

      Currency Converter

      +

      + Base currency: + + Converted to currency: + + Amount: + +

      +
      + + diff --git a/exercises/solutions/05_client/api_client/script.js b/exercises/solutions/05_client/api_client/script.js new file mode 100644 index 0000000..3177fef --- /dev/null +++ b/exercises/solutions/05_client/api_client/script.js @@ -0,0 +1,79 @@ + +'use strict' + +/*eslint max-statements: "off"*/ + +const baseURL = 'https://api.exchangeratesapi.io/latest' + +document.addEventListener('DOMContentLoaded', async() => { + console.log('page loaded') + await createDropdown() + document.querySelector('select[name="base"]').onchange = getRates + document.querySelector('select[name="converted"]').onchange = getRates + document.querySelector('input[name="amount"]').onkeyup = getRates +}) + +// populates the currency dropdown with a list of currency codes. +async function createDropdown() { + const data = await makeRequest(`${baseURL}`) + data.rates.EUR = 1.0 + const select = document.querySelector('select[name="base"]') + const select2 = document.querySelector('select[name="converted"]') + Object.keys(data.rates).sort().forEach( val => { + const node = document.createElement('option') + const node2 = document.createElement('option') + node.appendChild(document.createTextNode(val)) + node2.appendChild(document.createTextNode(val)) + select.appendChild(node) + select2.appendChild(node2) + }) +} + +// retrieves the value from the dropdown options list +async function getRates() { + const e = document.querySelector('select[name="base"]') + const base = e.options[e.selectedIndex].value + const e2 = document.querySelector('select[name="converted"]') + const converted = e2.options[e2.selectedIndex].value + const e3 = document.querySelector('input[name="amount"]') + const amount = Number(e3.value) + const table = document.querySelector('table') + table.innerHTML = '' + if(base && converted && amount) { + document.querySelector('h2').innerHTML = `Base Currency: ${base}` + const data = await makeRequest(`${baseURL}?base=${base}&symbols=${converted}`) + //buildTable(data.rates) + const e4 = document.querySelector('h2#result') + e4.innerHTML = `${Math.floor(data.rates[converted] * amount)} ${converted}` + } +} + +// creates a new html table with the data from the parameter +function buildTable(rates) { + const table = document.querySelector('table') + Object.keys(rates).sort().forEach( val => { + if(rates[val] !== 1) { + const row = table.insertRow(-1) + row.insertCell(0).innerHTML = val + row.insertCell(1).innerHTML = rates[val] + } + }) +} + +// this function converts the standard XMLHTTPRequest to a promise +const makeRequest = (url, method = 'GET') => new Promise( (resolve, reject) => { + const ok = 2 + const percent = 100 + const xhr = new XMLHttpRequest() + xhr.open(method, url) + xhr.onload = function() { + if ( Math.floor(this.status/percent) !== ok) { // if response code does not start with 2 + return reject({status: this.status, statusText: xhr.statusText }) + } + return resolve(JSON.parse(xhr.response)) + } + xhr.onerror = function() { + return reject({ status: this.status, statusText: xhr.statusText }) + } + xhr.send() +}) diff --git a/exercises/solutions/05_client/api_client/style.css b/exercises/solutions/05_client/api_client/style.css new file mode 100644 index 0000000..074f019 --- /dev/null +++ b/exercises/solutions/05_client/api_client/style.css @@ -0,0 +1,11 @@ + +body { + font-family: verdana; +} + +tr { + background-color: #fff; +} +tr:hover { + background-color: #f3f8aa; +} From 6e71b55273524f551ac7aeb5230408b583d1d53b Mon Sep 17 00:00:00 2001 From: "Chris Bass (aa6164)" Date: Mon, 18 Nov 2019 11:40:52 +0000 Subject: [PATCH 3/6] swapped the kill process line to be last --- exercises/03_acceptance/todo/test.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/03_acceptance/todo/test.sh b/exercises/03_acceptance/todo/test.sh index 3249ddd..86123ae 100755 --- a/exercises/03_acceptance/todo/test.sh +++ b/exercises/03_acceptance/todo/test.sh @@ -8,6 +8,5 @@ rm -rf *.db # [ ! -d "node_modules" ] && echo "INSTALLING MODULES" && npm install node index.js& node_modules/.bin/jest --runInBand --detectOpenHandles acceptance\ tests/* -kill %1 read -p "Press enter to continue" - +kill %1 From d7b239d0934021bc87d9594ae89c5b1d30635f77 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Mon, 18 Nov 2019 16:38:32 +0000 Subject: [PATCH 4/6] Added Solution Files --- exercises/solutions/05_client/todo/index.html | 24 +++++ exercises/solutions/05_client/todo/script.js | 93 +++++++++++++++++++ exercises/solutions/05_client/todo/style.css | 15 +++ 3 files changed, 132 insertions(+) create mode 100644 exercises/solutions/05_client/todo/index.html create mode 100644 exercises/solutions/05_client/todo/script.js create mode 100644 exercises/solutions/05_client/todo/style.css diff --git a/exercises/solutions/05_client/todo/index.html b/exercises/solutions/05_client/todo/index.html new file mode 100644 index 0000000..0936587 --- /dev/null +++ b/exercises/solutions/05_client/todo/index.html @@ -0,0 +1,24 @@ + + + + + + + + ToDo List + + + + + + +

      ToDo List

      +
      + + + +
      +
        +
        + + diff --git a/exercises/solutions/05_client/todo/script.js b/exercises/solutions/05_client/todo/script.js new file mode 100644 index 0000000..997a9e3 --- /dev/null +++ b/exercises/solutions/05_client/todo/script.js @@ -0,0 +1,93 @@ + +'use strict' + +let tableVisible = false + +const itemCol = 0 +const qtyCol = 1 +const actCol = 2 + +document.addEventListener('DOMContentLoaded', () => { + console.log('page loaded') + document.querySelector('input[name="item"]').focus() + document.querySelectorAll('input').forEach( elem => elem.onkeyup = addText ) + document.querySelector('form').onsubmit = addItem + document.querySelector('table').onclick = deleteRow +}) + +function addText(e) { + const enterKey = 13 + if(e.which === enterKey) { + if (e.target.name === 'item') { // if enter on item, focus on qty + document.querySelector('input[name="qty"]').focus() + } else { + addItem(e) // if enter on qty add the item + } + } +} + +function addItem() { + console.log('addItem') + const item = document.querySelector('input[name="item"]').value + const qty = document.querySelector('input[name="qty"]').value + + let indexOfDuplicateItem = null + + document.querySelectorAll('tr').forEach( (element, index) => { + if(element.childNodes[0].innerHTML === item) { + indexOfDuplicateItem = index + if (confirm(`Are you sure you want to add this item again: ${item}?`)) { + clearInput() + updateNode(indexOfDuplicateItem, qty) + } + } + }) + + if (indexOfDuplicateItem === null) { + clearInput() + if(item.length && qty) addNode(item, qty) + return false // prevents the form from submitting + } +} + +function updateNode(index, qty) { + const table = document.querySelector('table') + const row = table.rows[index] + const currentQty = Number(row.cells[1].innerHTML) + row.cells[1].innerHTML = Number(qty) + currentQty + console.log('update the table at index: ' + index + ' with qty: ' + qty) +} + +function clearInput() { + document.querySelector('input[name="item"]').value = '' + document.querySelector('input[name="item"]').focus() + document.querySelector('input[name="qty"]').value = '' +} + +function addNode(item, qty) { + const table = document.querySelector('table') + if (!tableVisible) addHeaders() + const row = table.insertRow(-1) // inserts row at the end of the table + row.insertCell(itemCol).innerHTML = item + row.insertCell(qtyCol).innerHTML = qty + row.insertCell(actCol).innerHTML = 'delete' +} + +function addHeaders() { + const table = document.querySelector('table') + tableVisible = true + const row = table.insertRow(0) + row.insertCell(itemCol).innerHTML = 'Item' + row.insertCell(qtyCol).innerHTML = 'Quantity' + row.insertCell(actCol).innerHTML = 'Action' +} + +function deleteRow(e) { + console.log('delete row') + if (e.target.innerHTML === 'delete') { + const index = e.target.parentElement.parentElement.rowIndex + const item = e.target.parentElement.parentElement.firstChild.innerHTML + if (confirm(`Are you sure you want to delete ${item}?`)) document.querySelector('table').deleteRow(index) + } + document.querySelector('input[name="item"]').focus() +} diff --git a/exercises/solutions/05_client/todo/style.css b/exercises/solutions/05_client/todo/style.css new file mode 100644 index 0000000..2af815b --- /dev/null +++ b/exercises/solutions/05_client/todo/style.css @@ -0,0 +1,15 @@ + +body { + font-family: verdana; +} + +table { + margin: 20px; + border-spacing: 0; +} + +td { + border: 1px solid grey; + margin: 0; + padding: 5px; +} From 60e920c8fc912e92e095711f6560805ab2ed5b76 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Mon, 18 Nov 2019 16:44:53 +0000 Subject: [PATCH 5/6] Reset Task Code --- exercises/05_client/todo/index.html | 4 ++-- exercises/05_client/todo/script.js | 22 +++++++++++++++------- exercises/05_client/todo/style.css | 2 -- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/exercises/05_client/todo/index.html b/exercises/05_client/todo/index.html index 2f74f9f..7cade38 100644 --- a/exercises/05_client/todo/index.html +++ b/exercises/05_client/todo/index.html @@ -13,8 +13,8 @@

        ToDo List

        -
        - + +
          diff --git a/exercises/05_client/todo/script.js b/exercises/05_client/todo/script.js index b3afa28..d22335a 100644 --- a/exercises/05_client/todo/script.js +++ b/exercises/05_client/todo/script.js @@ -1,12 +1,17 @@ 'use strict' +let tableVisible = false + +const itemCol = 0 +const qtyCol = 1 +const actCol = 2 + document.addEventListener('DOMContentLoaded', () => { console.log('page loaded') - // document.querySelectorAll('td').onclick = deleteRow + document.querySelector('input[name="item"]').focus() document.querySelectorAll('input').forEach( elem => elem.onkeyup = addText ) document.querySelector('form').onsubmit = addItem - // document.querySelector('table').onclick = deleteRow document.querySelector('table').onclick = deleteRow }) @@ -25,17 +30,20 @@ function addItem() { itemField.focus() if(item.length) addNode(item) return false // prevents the form from submitting + +} + +function clearInput() { + document.querySelector('input[name="item"]').value = '' + document.querySelector('input[name="item"]').focus() } -function addNode(item) { - // const node = document.createElement('li') // Create a
        1. node - // const textnode = document.createTextNode(item) // Create a text node - // node.appendChild(textnode) // Append the text to
        2. - // document.querySelector('ol').appendChild(node) +function addNode(item, qty) { const table = document.querySelector('table') const row = table.insertRow(-1) row.insertCell(0).innerHTML = item row.insertCell(1).innerHTML = 'delete' + clearInput() } function deleteRow(e) { diff --git a/exercises/05_client/todo/style.css b/exercises/05_client/todo/style.css index bfe701f..2af815b 100644 --- a/exercises/05_client/todo/style.css +++ b/exercises/05_client/todo/style.css @@ -13,5 +13,3 @@ td { margin: 0; padding: 5px; } - - From 93ab433333483e5bb1b3067c9fef72d5eb8801fa Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Mon, 18 Nov 2019 16:50:56 +0000 Subject: [PATCH 6/6] Finished DOM Notes --- 05 The DOM.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/05 The DOM.md b/05 The DOM.md index 8d8e9c3..661a714 100644 --- a/05 The DOM.md +++ b/05 The DOM.md @@ -5,13 +5,55 @@ In all the previous labs you have been writing JavaScript code to run on the ser ## 1 The DOM +xxx + +### 1.1 Getting a Refrerence to a DOM Element + +One of the first tasks you will face when working with the DOM is getting a reference to the correct DOM object. There are two approaches. + +#### 1.1.1 Direct Reference + +The simplest way is to use a _CSS Selector_ to get a reference to the element or elements you want to manipulate. + +1. The `document.querySelector()` function takes a string parameter which is the _CSS Selector_ that can identify the DOM objects you want to reference. For example `document.querySelector('h1')` returns a reference to the top-level heading on the page. If there are more than one element matching the CSS selector it returns the first match. +2. If you want to get a reference to multiple elements such as all the list items you should use the `document.querySelectorAll()` function which returns an array of all the matching DOM objects. For example `document.querySelectorAll('li')` returns an array of all the items in all lists on the page. + +#### 1.1.2 The Event Object + +When an event handler is _fired_, the function called is passed a single parameter , often labelled as `e` which is an object representing the _event_. This contains a property called `target` which stores the DOM of the element that triggered the event itself. So, for example, if you clicked on an item in a list to trigger the event, the `target` would contain a reference to the `li` element. This is useful when you want to use the same event handler to handle events triggered by multiple elements such as items in a list. + +For example both lines of code below get a reference to the first table cell on the first row. + +```javascript +document.querySelectorAll('tr')[0].childNodes[0].innerHTML +document.querySelector('table').rows[0].childNodes[0].innerHTML +``` + +### 1.2 Traversing the DOM + +When you are working with the DOM you will frequently need to access the parent DOM element or perhaps the child one. This is called _traversing the DOM_ and there are a number of important properties and functions you will need to use: + +1. To access the _parent_ node (such as the `ol` node when you select a `li` node) you use the `parentElement` property. +2. To access a _child_ node (for example the `li` node if you are referencing the `ol` node) you will need to make use of the `childNodes` array. For example to access the third item in the list you would use the expression `.childNodes[2]`. If there is only a single child node you can use the `firstChild` property instead. +3. Once you have a reference to the desired DOM node you will often want to access the text inside it. This can be accessed using the `innerHTML` property. + +By chaining these together you can navigate both up and down the DOM tree to locate the DOM object you want, for example the following returns the contents of the first cell of the table row regardless of which cell in the row was clicked. + +```javascript +e.target.parentElement.firstChild.innerHTML +``` + ### 1.1 Test Your Understanding -1. When the user tries to delete an item, use the `alert()` function to ask if they are sure. -2. When a row is deleted, bring the focus back to the input box. +1. When the user tries to delete an item, use the `confirm()` function to ask if they are sure. This returns true if the user agrees. + 1. Display the name of the item being deleted in the confirmation dialog using one of the three ways to access the table DOM. +2. When the confirm dialog closes, bring the focus back to the input box. + 1. If the user clicks on any of the cells the focus shound return to the input box. 3. Use the `e.target.innerHTML` property to prevent the row being deleted unless the user clicks on the `delete` link. 4. Add a second textbox called `qty`, this will hold the number of items being added. -5. Add a second column to the table to display the quantity of each item. + 1. When the item name is added and the enter key pressed focus should move to the qty field. + 2. When a quantity is entered and the enter key pressed the data should be added to the table. +5. Add a column between the item name and the delete link (making a total of 3 columns) to the table to display the quantity of each item. 6. Prevent items being added if they are already in the list. 7. When a duplicate item is added increase the qty of the item already in the list.