0

I am trying to make use of createElement, createTextNode and appendChild to rewrite an outdated simple to-do list code example.

The code example requires the use of the array join() method so, unfortunately, this can't be removed. The old version just wrote the HTML for the ul list in code fragments.

I am unsure how to proceed where I have entered to comment at line 30 of the js: " need to render the tasks (from stringToInsert) as a list to div id="output" here "

I have referred to the following stackoverflow articles to help me rewrite the code: js-how-to-concatenate-variables-inside-appendchild -This example uses join() and appendChild but not list items.

create-ul-and-li-elements-in-javascript- From that one, I copied the code from a Fiddle and put it into function createul() in my example codepen

Once I have the function addTask() working createul() and it's associated HTML elements (such as the render list button) will be removed.

// tasks.js #2
// This script manages a to-do list.

// Need a global variable:
var tasks = [];


function addTask() {
  'use strict';

  console.log("addTask started");
  // Get the task:
  var task = document.getElementById('task');

  // Reference to where the output goes:
  var output = document.getElementById('output');

  if (task.value) {
    tasks.push(task.value);
    // Update the page:
    //var message = '<h2>To-Do</h2>';

    var stringToInsert = tasks.join(' : ');
    console.log(stringToInsert);

    var taskUl = document.createElement('ul');
    taskUl.setAttribute('id', 'autoTask');

    document.getElementById('output').appendChild(taskUl);
    /* need to render the tasks (from stringToInsert) as a list to div id ="output" here */

    document.getElementById("task").value = '';
  }

  // Return false to prevent submission:
  return false;

} // End of addTask() function.




function createul() {
  var ul = document.createElement('ul');
  ul.setAttribute('id', 'proList');

  var t, tt;
  productList = ['Electronics Watch', 'House wear Items', 'Kids wear', 'Women Fashion'];

  document.getElementById('renderList').appendChild(ul);
  productList.forEach(renderProductList);

  function renderProductList(element, index, arr) {
    var li = document.createElement('li');
    li.setAttribute('class', 'item');

    ul.appendChild(li);

    t = document.createTextNode(element);

    li.innerHTML = li.innerHTML + element;
  }
}

function init() {

  document.getElementById('renderbtn').addEventListener("click", createul);

  document.getElementById('theForm').onsubmit = addTask;

}


window.addEventListener('load', init);
/* css (I have simplified this a little for this example and I am sorry I haven't cut it down further) */

form {
  margin: 0 auto;
  width: 400px;
  padding: 14px;
  background-color: #ffffff;
  border: solid 2px #425955;
}


/* ----------- stylized ----------- */

h1 {
  font-size: 14px;
  font-weight: bold;
  margin-bottom: 8px;
}

p {
  font-size: 11px;
  color: #666666;
  margin-bottom: 20px;
  border-bottom: solid 1px #BFBD9F;
  padding-bottom: 10px;
}

label {
  display: block;
  font-weight: bold;
  text-align: right;
  width: 140px;
  float: left;
}

select {
  float: left;
  font-size: 12px;
  padding: 4px 2px;
  border: solid 1px #BFBD9F;
  width: 200px;
  margin: 2px 0 20px 10px;
}

input {
  float: left;
  font-size: 12px;
  padding: 4px 2px;
  border: solid 1px #BFBD9F;
  width: 200px;
  margin: 2px 0 20px 10px;
}

#submit {
  clear: both;
  margin-left: 150px;
  width: 125px;
  height: 31px;
  background: #F1F2D8;
  text-align: center;
  line-height: 20px;
  color: #000000;
  font-size: 12px;
  font-weight: bold;
}

#output {
  clear: both;
  margin-bottom: 10px;
  color: blue;
}
<form action="#" method="post" id="theForm">

  <div><label for="task">Task</label><input type="text" name="task" id="task" required></div>
  <input type="submit" value="Add It!" id="submit"><br>

  <button type="button" id="renderbtn">render list</button>

  <div id="renderList"></div>
  <div id="output"></div>

edit: I can just convert it back to an array with something like the following if there is no other way of doing it.

var ar = stringToInsert.split(' : ');

or something based on:

stringToInsert.split(' : ').forEach ... or if that doesn't work I could try map()

caston
  • 159
  • 12
  • 1
    https://stackoverflow.com/questions/63509328/understanding-html-form-element-behavior/63509329#63509329 Returning `false` from the handler function doesn't prevent the submission of the form. That works only on an inline listener code. – Teemu Sep 19 '20 at 07:10
  • I had a huge problem that the page would refresh to blank before I started using it but you are right it's not happening now when I comment it out. I will look into it further. – caston Sep 19 '20 at 07:17
  • The original code example (not included) had it in the same place. – caston Sep 19 '20 at 07:29
  • 1
    The fix is simple, if you don't want to submit a form, then don't submit it. Use a button type of button, and listen clicks on that button instead of listening submit event of the form. – Teemu Sep 19 '20 at 07:34
  • 100% agree but I'm only allowed to rewrite the js here. – caston Sep 19 '20 at 07:38
  • 1
    Then just include the event object argument in the argument list of the handler function, and call its `preventDefault` method. Notice also, that you can change the type of the button in JS too ... – Teemu Sep 19 '20 at 07:42

1 Answers1

1

I'm going to show you a different approach that may help clear things up -

function ul (nodes)
{ const e = document.createElement("ul")
  for (const n of nodes)
    e.appendChild(n)
  return e
}

function li (text)
{ const e = document.createElement("li")
  e.textContent = text
  return e
}

function onSubmit (event)
{ event.preventDefault()
  tasks.push(f.taskInput.value)
  f.taskInput.value = ""
  render()
}

function render ()
{ const newList = ul(tasks.map(li))
  f.firstChild.replaceWith(newList)
}

const tasks = [ "wash dishes", "sweep floors" ]  // <- initial tasks
const f = document.forms.main                    // <- html form
f.addButton.addEventListener("click", onSubmit)  // <- button listener
render()                                         // <- first render
<h3>todo list</h3>
<form id="main">
  <ul></ul>
  <input name="taskInput" placeholder="example: paint fence">
  <button name="addButton">Add Task</button>
</form>

And here's a more modern approach using a DOM library like React -

const { useState, useRef } = React
const { render } = ReactDOM

function TodoList ({ initTasks = [] })
{ const [ tasks, updateTasks ] =
    useState(initTasks)
    
  const inputEl =
    useRef(null)
  
  function onSubmit () {
    updateTasks([ ...tasks, inputEl.current.value ])
    inputEl.current.value = ""
  }
  
  return <div>
    <h3>todo list</h3>
    <ul>{tasks.map(t => <li children={t} />)}</ul>
    <input ref={inputEl} placeholder="ex: paint fence" />
    <button onClick={onSubmit}>add</button>
  </div>
}
 
render
  ( <TodoList initTasks={[ "wash dishes", "sweep floors" ]}/>
  , document.body
  )
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • This very impressive. Very clean and tidy code and I could learn a lot from it. Thank you. – caston Sep 19 '20 at 14:27
  • I notice the tasks.map on line 22 in the render function. I was thinking of using the map array function if I couldn't use forEach and you give a better example then I could have ever imagined. – caston Sep 19 '20 at 14:51
  • I'm delighted to help. Best of luck on your JS journey! – Mulan Sep 19 '20 at 20:15
  • I noticed you are not using ';'. For example: const tasks = [ ] // <- initial tasks const f = document.forms.main // <- html form f.addButton.addEventListener("click", onSubmit) // <- button listener render() instead of: const tasks = [ ]; // <- initial tasks const f = document.forms.main; // <- html form f.addButton.addEventListener("click", onSubmit); // <- button listener render() Why is this? – caston Sep 20 '20 at 00:31
  • 1
    Sharp eyes, nice catch! JavaScript has [automatic semicolon insertion](https://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi) and in most cases you totally don't need them. As a functional style advocate, when I need `;` in JS, I sense that as a problem that I need to restructure my program. – Mulan Sep 20 '20 at 00:58