-1

I have a ul (unordered list) with many li's. The li's are created dynamically. So the click event listener needs to be implemented dynamically. This is easy to do in jQuery;

$(document).on("click", ".checkboxes", function() {     
   var boxId = $(this).attr("id");
   var num = boxId.split("-")[1];
   console.log("checkbox: "+num);
  // checkedItem(num);
   var checkedLi = document.getElementById("li-"+num);
   checkedLi.style.textDecoration = "line-through";
}); 

however, I want my app to be fully in javascript. If someone can provide an answer. Appreciated heres the app online http://pctechtips.org/apps/todo/ inde.html

<!DOCTYPE html>
<html>
<head>
    <title>TodoList App</title>
    <!-- bootstrap cdn -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- google fonts -->
    <link href="https://fonts.googleapis.com/css?family=Righteous" rel="stylesheet">

    <style type="text/css">
        /*variables*/
        :root {
            --righteous-font: 'Righteous', cursive;
        }

        body {
            /*background-color: #536691;*/
            background-image: url("http://pctechtips.org/apps/conf/img/Chicago-Wallpaper-3.jpg");
            min-height: 100vh;
            z-index: -10;
            background-position: center;
            background-repeat: no-repeat;
            background-size: cover;
            color: white;
            font-size: 1.3rem;          
        }
        .hero {
            position: absolute;
            min-height: 100vh;
            min-width: 100vw;
            top: 0;
            bottom: 0;
            background-color: rgba(31, 34, 118, 0.5);
        }

        h1 {
            margin-top: 12rem;
            margin-bottom: 0px;
            padding-bottom: 0px;
            font-family: var(--righteous-font);
        }

        .lead {
            font-size: 1.5rem;
            font-family: var(--righteous-font);
        }
        hr {
            margin-top: 2rem;
            border: 1px solid white;
            display: none;
        }
        ul {
            /*border-top: 2px solid white;*/
            margin-top: 2rem;
            list-style: none;
            /*display: none;*/
        }
        li {
            border-bottom: 1px solid white;
            padding: 0.5rem 0 0.5rem 0;
            margin: 0 1rem 0 1rem;
        }
        .checkboxes {
            float: right;
            line-height: 15px;
            width: 17px;
            height: 17px;
            background-color: #e9ecef;
            border: 1px solid #e9ecef;
            border-radius: 3px;
        }
    </style>
</head>

<body>
    <div class="hero">
        <div class="container">
            <h1 class="display-2 text-center">TodoList</h1>
            <p class="lead text-center">Welcome to my todoList applications</p>
            <div class="row">
                <form id="form" class="col-8 mx-auto">
                    <div class="input-group">
                        <input id="input" class="form-control" placeholder="Enter todo list item" value="this is a todo list item for me todo">
                        <span>
                            <button id="btn" type="button" class="btn btn-primary">Submit</button>
                        </span>
                    </div>               
                </form>
            </div>  
            <hr>        
            <div class="row">               
                <ul id="list" class="list col-8 mx-auto">
                    <!-- <li>this is a todo item <input type="checkbox" class="checkbox"></li>
                    <li>this is a todo item <input type="checkbox" class="checkbox"></li> -->
                </ul>
            </div>

        </div>
    </div>

    <!-- todolist app functionality -->
    <script type="text/javascript">
        window.onload = function() {
            // variables
            var submitBtn = document.getElementById("btn");
            var ul = document.getElementById("list");
            var todoItem = document.getElementById("input");
            var hr = document.getElementById("hr");
            var id = 1;

            //button event listener
            submitBtn.addEventListener("click", addTodoItem);

            //dynamically added checkbox event listener
            $(document).on("click", ".checkboxes", function() {     
                var boxId = $(this).attr("id");
                var num = boxId.split("-")[1];
                console.log("checkbox: "+num);
                // checkedItem(num);
                var checkedLi = document.getElementById("li-"+num);
                checkedLi.style.textDecoration = "line-through";
            }); 


            /* add todo items to list */
            function addTodoItem() {
                console.log(todoItem.value);
                if(todoItem.value === "") {
                    alert("Enter some text!");
                }
                else {
                    if(ul.style.borderTop == "") {
                        ul.style.borderTop = "2px solid white";
                    }                   
                    var li = document.createElement("li");
                    li.id = "li-"+id
                    var text = document.createTextNode(todoItem.value);
                    var checkbox = document.createElement("input");
                    checkbox.id = "checkbox-"+id;
                    checkbox.className = "checkboxes";
                    checkbox.type = "checkbox";
                    li.appendChild(text);
                    li.appendChild(checkbox);
                    ul.appendChild(li);
                    id++;
                }               
                //reset form
                document.getElementById("form").reset();
            }

            /* check item completed */
            function checkedItem(num) {

            }

        }       

    </script>
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
</body>
</html>
miatech
  • 2,150
  • 8
  • 41
  • 78
  • First off. Make sure you focus on input on page load. so user can just type and hit enter. Second, make sure you focus on input after pressing enter. I have made many applications like this. My guess is you are wanting to allow the user to add to the LI with every enter, but then you want to allow the use to delete those dynamically added items? – technology101010 Feb 23 '18 at 21:11
  • 1
    You likely mean "pure JavaScript" as jQuery is of course implemented in JavaScript. Basically, you do the same thing as jQuery; you attach an event handler to the document, then check the `event` argument passed in; specifically checking if its `target` matches your selector. – Heretic Monkey Feb 23 '18 at 21:11
  • 1
    Also, check out [this answer to the same question](https://stackoverflow.com/a/27373951/215552). – Heretic Monkey Feb 23 '18 at 21:13

2 Answers2

3

As jQuery is JavaScript, you don't need jQuery. When window loads, listen for a click on your list - not checkboxes. In event listener's callback function, check if the checkbox was the target, then get this particular li element and apply your css.

//get list and listen for click on your list
const list = document.getElementById("list");
list.addEventListener("click", function(event) {
  const target = event.target;
  //ignore clicks on anything but checkbox
  if(target.type !== "checkbox") return;
  //apply css here
  target.parentNode.style.textDecoration = "line-through";
});

Since checkboxes are children of li's, and li's are children of ul, click event will bubble from checkbox element up to your list, making this solution possible.

Edit after conversation in comments: glad we came into understanding.

Tomasz Bubała
  • 2,093
  • 1
  • 11
  • 18
  • event listener is attached to list - callback checks if click target was a checkbox, how is that wrong? Please elaborate – Tomasz Bubała Feb 23 '18 at 21:59
  • I did and I understand the concept. You can't attach event listener to all checkboxes, since there might be other checkboxes in the document, not related to to-do list from example. Querying for element with `checkboxes` class and attaching separate event listeners would also fail, since they're added dynamically. Solution left is to listen for click on the list, and check if click target was actually a checkbox and my code is doing just that. I still don't know what you mean - please post your solution to this problem. – Tomasz Bubała Feb 23 '18 at 22:07
  • https://jsfiddle.net/fL35bsLu/1/ - here is the fiddle. Open console and click on a checkbox. You said it's never the target and you're wrong. – Tomasz Bubała Feb 23 '18 at 22:13
  • Sorry, I misunderstood the program. – Sreekanth Reddy Balne Feb 23 '18 at 22:14
  • my vote is locked unless the answer is edited. So please add something. – Sreekanth Reddy Balne Feb 23 '18 at 22:16
  • I edited my answer so you can take the downvote back :) glad we solved this – Tomasz Bubała Feb 23 '18 at 22:18
0

You don't need JavaScript to control styles of elements when using interactive elements like checkbox.

Using:

1) pseudo-element ::after,

2) Adjacent Sibling Combinator,

3) pseudo-class :checked, and

4) the [for] attribute

allows us to control interactive elements like form controls. Checkbox behavior is like OP:

  • checked and line-through check

  • and Font-Awesome icons as a bonus

  • No JavaScript is involved.

Although a CSS solution is ok for a TODO list it is not as versatile as JavaScript. In the future you'll need to deal with event handling of elements other than form controls so I added a test function to demonstrate Event Delegation.

  • The form listens for an event for itself and its children.

  • The Event Object property Event.currentTarget is the form.

  • Event.target is the origin of event (ex. clicked button).

  • e.target can be any of form's children so by using conditions (if, if else etc), and Event Object properties we can delegate event handling for an unknown and unlimited amount of elements with just one parent/ancestor element.

  • HTMLFormControlsCollection API makes forms easier to access One of the functions uses Template Literals instead of literal string and insertAdjacentHTML() method.

Note in CSS: the doubling of selectors is to enforce strong specificity. I tend to do this when dealing with Bootstrap because Bootstrap's stylesheets are very difficult to override.

Details commented in Demo

Demo

/* HTMLFormControlsCollection API makes forms easier to access*/
var idx = 0;
var lst = document.getElementById('lst');
var fX = document.forms.frm;
var xF = fX.elements;
var btn = xF.btn;
var txt = xF.txt;

btn.addEventListener('click', addTask, false);

/* This function uses Template Literals instead of literal string
|| and insertAdjacentHTML() method.
*/
function addTask(e) {
  idx++;
  var text = txt.value;
  var item = `<li id="li${idx}"class='item'>
                <input id="chx${idx}" class='chk' type="checkbox">
                <label for="chx${idx}" class='fa'>${text}</label>
              </li>`;
  text.value = "";
  lst.insertAdjacentHTML("beforeend", item);
}


/* This is to demonstrate Event Delegation 
|| The form listens for an event for itself and its children.
|| The Event Object property Event.currentTarget is the form.
|| Event.target is the origin of event (ex. clicked button).
|| e.target can be any of form's children so by using 
|| conditions (if, if else etc), and Event Object properties
|| we can delegate event handling for an unknown amount of
|| elements with just one parent/ancestor element.

ADD/DEL last slash on next line to toggle eventListeners 
/*/
fX.addEventListener('click', testEvent);
/*/
document.addEventListener('click', testEvent);
//*/

function testEvent(e) {
  if (e.target !== e.currentTarget) {
    console.log(e.target.tagName + ' ' + e.type);
  }
}
:root {
  --r: 'Righteous', cursive;
  margin: 0;
  padding: 0;
  border: 0
}

body {
  /*background-image: url("http://pctechtips.org/apps/conf/img/Chicago-Wallpaper-3.jpg");*/
  min-height: 100vh;
  z-index: -10;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  color: white;
  font-size: 1.3rem;
}

.hero {
  min-height: auto;
  min-width: 100vw;
  background-color: rgba(31, 34, 118, 0.5);
}

h1 {
  margin-top: 12rem;
  margin-bottom: 0px;
  padding-bottom: 0px;
  font-family: var(--r);
}

.lead {
  font-size: 1.5rem;
  font-family: var(--r);
}

#frm hr {
  margin: 0;
  outline: 1px solid white;
}

ul {
  margin-top: 2rem;
  list-style: none;
}

li {
  border-bottom: 1px solid white;
  height: 32px;
  padding-top: 5px
}

#frm input,
#frm label {
  font: 400 1.2rem/1.4 Quicksand;
}
/* Using 1) pseudo-element ::after,  2) Adjacent Sibling
|| Combinator, 3) pseudo-class :checked, and 4) the [for]
|| attribute allows us to control interactive elements like form || controls. Checkbox behavior is like OP: checked and 
|| line-through check and Font-Awesome icons as a bonus involved
|| no JavaScript.
*/
/* Note: the doubling of selectors is to enforce strong
|| specificity. I tend to do this when dealing with Bootstrap
|| because Bootstrap's stylesheets are very difficult to 
|| override.
*/
.chk.chk {
  display: none;
}

.fa.fa {
  font: inherit;
  background-color: #e9ecef;
  border: 1px solid #e9ecef;
  padding: 0 0 0 5px;
  border-radius: 3px;
}

.fa.fa::after {
  float: right;
  content: "\f096";
  font: 400 24px/1 FontAwesome;
  width: 24px;
  height: 24px;
  margin: 0 0 0 5px;
}

.chk.chk:checked+.fa.fa {
  text-decoration: line-through;
}

.chk.chk:checked+.fa.fa::after {
  content: "\f046"
}

/* For demo only */
.as-console-wrapper {
  width: 45%;
  margin-left: 55%;
  max-height: 32px !important
}

.as-console-row {font-size:0;}
<!DOCTYPE html>
<html>

<head>
  <title>TodoList App</title>
  <!-- bootstrap cdn -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  <!-- google fonts -->
  <link href="https://fonts.googleapis.com/css?family=Quicksand|Righteous" rel="stylesheet">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
</head>

<body>
  <div class="hero">
    <div class="container">
      <h1 class="display-2 text-center">TodoList</h1>
      <p class="lead text-center">Welcome to my todoList applications</p>
      <div class="row">
        <form id="frm" class="col-8 mx-auto">
          <div class="input-group">
            <input id="txt" class="form-control" placeholder="Enter todo list item" value="">

            <button id="btn" type="button" class="btn btn-primary">Submit</button>
          </div>
        </form>
      </div>
      <hr>
      <div class="row">
        <ul id="lst" class="list col-8 mx-auto">
        </ul>
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
</body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68