Alright so I hope Im just missing something easy. Basically I am making a todo list where I want to have a checkbox appear with each list item (this works). When users click on the checkbox, the textDecoration = "line-through" is supposed to go through the listItem class. Meaning a line goes through the todo task the person creates. Here is the code I am mainly working with:
function show() {
var todos = get_todos();
var html = '<ul>';
for(var i=0; i < todos.length; i++) {
html += '<span><li class="listItem" style="float:left;">' + todos[i] + '</li><input class="checkBox" type="checkbox">' + '<button class="remove" id="' + i + '"><i class="fa fa-trash" aria-hidden="true"></i></button></span><br/>';
};
html += '</ul>';
document.getElementById('todos').innerHTML = html;
var buttons = document.getElementsByClassName('remove');
for (var i=0; i < buttons.length; i++) {
buttons[i].addEventListener('click', remove);
};
////////////////////////////////////
//Everything above here works. Below is the checkbox issue
var checkBox = document.getElementsByClassName("checkBox");
var listItem = document.getElementsByClassName("listItem");
for (var i=0; i < checkBox.length; i++) {
if (checkBox.checked == true){
listItem.style.textDecoration = "line-through";
}else {
listItem.style.textDecoration = "none";
}
};}
Where I'm at now is if I create an onClick function in the original checkbox, I can use that if/else statement and it kind of works. It will not work if I set the checkBox/listItems variables using document.getElementsByClassName but it will work if I use document.getElementById. The problem is that it only works on the first task that the user creates and none of the other ones created after. Im assuming that is either because Ids only work for one element (unlike a class that works for multiple) or because its not cycling through a for loop like it is in the code above.
TL/DR Basically when I run the code above I keep getting "Uncaught TypeError: Cannot set property 'textDecoration' of undefined
at show (todo.js:57)
at todo.js:75".
I dont get this error when I make a separate function for the checkbox and use elementbyid instead of elementsbyclass (changing the id/class of the html section above too)
I really want to get those line-through effects working on all tasks, not just the first one. Any ideas are greatly appreciated. Thanks everyone!
I would accomplish this with css instead of javascript (that's generally my rule, when possible). In this case you have to make one small change to your markup: since there's no previous-sibling selector, you'll have to put the inputs before the corresponding lis, but since the lis are float:left it still renders exactly the same.
input:checked + li {
text-decoration:line-through;
}
<ul>
<span><input class="checkBox" type="checkbox"><li class="listItem" style="float:left;">foo</li><button class="remove" id="' + i + '"><i class="fa fa-trash" aria-hidden="true"></i></button></span><br/>
<span><input class="checkBox" type="checkbox"><li class="listItem" style="float:left;">bar</li><button class="remove" id="' + i + '"><i class="fa fa-trash" aria-hidden="true"></i></button></span><br/>
<span><input class="checkBox" type="checkbox"><li class="listItem" style="float:left;">baz</li><button class="remove" id="' + i + '"><i class="fa fa-trash" aria-hidden="true"></i></button></span><br/>
</ul>
function show() {
var todos = get_todos();
var html = '<ul>';
for (var i = 0; i < todos.length; i++) {
html += '<span><li class="listItem" style="float:left;">' + todos[i] + '</li><input class="checkBox" type="checkbox">' + '<button class="remove" id="' + i + '"><i class="fa fa-trash" aria-hidden="true"></i></button></span><br/>';
};
html += '</ul>';
document.getElementById('todos').innerHTML = html;
var buttons = document.getElementsByClassName('remove');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', remove);
};
////////////////////////////////////
//Everything above here works. Below is the checkbox issue
var checkBox = document.getElementsByClassName("checkBox");
var listItem = document.getElementsByClassName("listItem");
for (var i = 0; i < checkBox.length; i++) {
if (checkBox[i].checked == true) {
listItem[i].style.textDecoration = "line-through";
} else {
listItem[i].style.textDecoration = "none";
}
};
}
You probably missed the index in the listItem and checkBox array
I think the problem is that checkBox is an array-like object. I think you know that as when you wrote the code you look at checkBox.length, however you then failed to index into the array.
You have:
var checkBox = document.getElementsByClassName("checkBox");
var listItem = document.getElementsByClassName("listItem");
for (var i=0; i < checkBox.length; i++) {
if (checkBox.checked == true){
listItem.style.textDecoration = "line-through";
}else {
listItem.style.textDecoration = "none";
}
};}
To make it clear I've pluralised the names where needed, as well as adding the index reference:
var checkBoxes = document.getElementsByClassName("checkBox");
var listItems = document.getElementsByClassName("listItem");
for (var i=0; i < checkBoxes.length; i++) {
if (checkBoxes[i].checked == true){
listItems[i].style.textDecoration = "line-through";
}else {
listItems[i].style.textDecoration = "none";
}
};}
Related
I have very little experience with Javascript.
My goal is to build a table from an array, and allow the user to delete items from the array as needed. I wanted to have a "delete" button next to each array item that is shown.
I'm open to other ideas of how to accomplish this. My code is here:
var LargeList = ["¥"]
function AddLargeItem() {
LargeList.push(document.getElementById("LargeItems").value)
LLen = LargeList.length;
text = "<ol>";
for (i = 0; i < LLen; i++) {
text += "<li>" + LargeList[i] + "</li>";
}
text += "</ol>";
document.getElementById("LargeInventory").innerHTML = text;
document.getElementById("LargeCount").innerHTML = LLen * 10;
document.getElementById("random").innerHTML = LargeList.join("/ ");
}
The target HTML:
<body>
<div>
<select id="LargeItems">
<option></option>
<option>LMIS</option>
<option>ARMC</option>
<option>BNCH</option>
</select>
<input type="button" onclick="AddLargeItem()" id="AddLargeItem" value="ADD LARGE" />
<span><p id="LargeCount"></p></span>
</div>
<p id="random"></p>
<p id="LargeInventory"></p>
</div>
I wanted to have a "delete" button next to each array item that is shown
To achieve your above requirement, you can modify the code as below.
<script>
var LargeList = ["¥"]
function AddLargeItem() {
LargeList.push(document.getElementById("LargeItems").value)
PopulateList(LargeList);
}
function PopulateList(arr) {
LLen = arr.length;
text = "<ol>";
for (i = 0; i < LLen; i++) {
text += "<li>" + arr[i] + "<input type='button' onclick='Delitem(" + i + ")' value='Delete' /></li>";
}
text += "</ol>";
document.getElementById("LargeInventory").innerHTML = text;
document.getElementById("LargeCount").innerHTML = LLen * 10;
document.getElementById("random").innerHTML = arr.join("/ ");
}
function Delitem(index) {
LargeList.splice(index, 1);
PopulateList(LargeList);
}
</script>
Test Result
There is two ways I can think of to do this.
num one:
assign the elements you want to delete and id ex: then use javascript to append the elements with your list. then onclick="remove('list')" and make a function like this
function remove(id){getElementById(id).remove()}
num two:
if you just want to use javascript def your list then get an integer from the user and use my something like this
index = getElementById('foo').value;
my_list = my_list[:foo] + my_list[foo+1:];
document.write(my_list);
This question already has answers here:
JavaScript DOM remove element
(4 answers)
Closed 3 years ago.
I am trying to make a simple list using html input box and js. the list are creating on clicking "add skill" and get removed when click on "remove". But when I try to add some skill after remove, the last removed item also get back.
var skillList="";
var i = 0;
function addSkill(){
var skills= document.getElementById("addSkill").value;
if(skills != ""){
skillList += "<li><span name='skillItem' id='skillItem"+ i +"'>" + skills + "</span> " +
"<a onclick='removeSkill()'>remove</a></li>";
i++;
document.getElementById("skill").innerHTML = skillList;
document.getElementById("addSkill").value="";
}
}
function removeSkill(){
skillList="";
var items = document.querySelectorAll("#skill li"),index,tab = [];
for(var j = 0; j < items.length; j++){
tab.push(items[j].innerHTML);
}
for(var j = 0; j < items.length; j++){
items[j].onclick = function(){
index = tab.indexOf(this.innerHTML);
items[index].parentNode.removeChild(items[index]);
tab.splice(j,1);
};
}
console.log(tab);
for(var j=0; j<tab.length;j++){
skillList += "<li>" + tab[j] + "</li>";
}
}
<td><label>skills:</label></td>
<td>
<ul id="skill"></ul>
<input type="text" name="skill" id="addSkill"/>
<a onclick="addSkill();" value="">add skill</a>
</td>
Just delete this piece of code
for(var j=0; j<tab.length;j++){
skillList += "<li>" + tab[j] + "</li>";
}
Your were adding it again...
Working now...
var skillList="";
var i = 0;
function addSkill(){
var skills= document.getElementById("addSkill").value;
if(skills != ""){
skillList += "<li><span name='skillItem' id='skillItem"+ i +"'>" + skills + "</span> " +
"<a onclick='removeSkill()'>remove</a></li>";
i++;
document.getElementById("skill").innerHTML = skillList;
document.getElementById("addSkill").value="";
}
}
function removeSkill(){
skillList="";
var items = document.querySelectorAll("#skill li"),index,tab = [];
for(var j = 0; j < items.length; j++){
tab.push(items[j].innerHTML);
}
for(var j = 0; j < items.length; j++){
items[j].onclick = function(){
index = tab.indexOf(this.innerHTML);
items[index].parentNode.removeChild(items[index]);
tab.pop(j,1);
};
}
}
<td><label>skills:</label></td>
<td>
<ul id="skill"></ul>
<input type="text" name="skill" id="addSkill"/>
<a onclick="addSkill();" value="">add skill</a>
</td>
You got all lost in the loops and indexes in your removeSkill function and it's way easier than what you are doing - one single line of code. No loops and no arrays are needed.
You've also got a bunch of other styles of coding that are ancient and should be avoided.
You don't need to use a hyperlink just because you want to give
someone something to click on. All visible elements support a click
event. Only use hyperlinks when you are actually navigating
somewhere.
You can't use table cells without a table.
Don't use inline HTML event attributes (onclick). Separate all your
JavaScript from your HTML and do the event binding with
.addEventListener().
Don't create new HTML by concatenating strings together. It becomes a
nightmare fast and requires you to use .innerHTML, which has
performance and security implications. Instead, create new DOM
objects, configure their properties as needed and then append them
into the document.
See the comments inline below:
// Get all the element references you'll need just once:
var skillList = document.querySelector("#skillList");
var newSkill = document.querySelector("#newSkill");
var btnAddSkill = document.querySelector("input[type='button'");
// Do all of your event binding in JavaScript, not with inline HTML event attributes
btnAddSkill.addEventListener("click", addSkill);
function addSkill(){
if(newSkill.value !== ""){
// Don't build new HTML by concatenating strings. Create elements and configure them as objects
var li = document.createElement("li");
li.textContent = newSkill.value;
// Only use hyperlinks for navigation, not to have something to click on. Any element can be clicked
var span = document.createElement("span");
span.classList.add("remove");
span.textContent = "remove skill";
span.addEventListener("click", removeSkill);
li.appendChild(span); // Add the span to the bullet
skillList.appendChild(li); // Add the bullet to the list
newSkill.value = "";
}
}
function removeSkill(){
// Just remove the closest <li> ancestor to the <span> that got clicked
skillList.removeChild(this.closest("li"));
}
.remove { display:inline-block; margin-left:10px; padding:2px 4px; background:rgba(200,0,225,.7); color:#ff0; cursor:pointer; }
li {margin:8px 0; border-bottom:1px dashed #e0e0e0; }
<h1>Skills:</h1>
<input type="text" name="skill" id="newSkill">
<input type="button" value="add skill">
<ul id="skillList"></ul>
Beginner here. I have a loop that creates 26 buttons with unique ID's and values. What I'm struggling with is figuring out the proper way to send the button's ID to a function so that I can store unique vars for each button independently without creating more than one function. I currently have an array with the 26 items I need for my buttons and the following loop:
function makeButtons() {
for (var i = 0; i < 26; i++) {
document.getElementById("whereButtonsGo").innerHTML += "<input type = 'button' value = '" + items[i] + "' id = 'button" + items[i] + "' onclick = doThing(button" + items[i] + ")'>";
}
}
I want the argument in the onclick function to be sent to a function such as:
function doThing(id) {
document.getElementById("'" + id.value + "'").style.color = "pink";
}
But so far I haven't been able to get this to work. Any help would be greatly appreciated!
Maybe this is what you are looking for:
makeButtons();
function makeButtons() {
for (var i = 0; i < 26; i++) {
document.getElementById("whereButtonsGo").innerHTML += "<input type = 'button' value = '" + i + "' onclick = doThing(this)>";
}
}
function doThing(currentButton) {
currentButton.style.color = "pink";
}
<div id="whereButtonsGo"/>
Try to keep the IDs as simple as possible
I recommend against using innerHTML for creating elements that you actually want to do something. Even if it works, your code will be amazingly unclear. Instead, write code that demonstrates that you're actually creating and adding elements:
var items = [1,2,3,4,5,6];
function makeButtons() {
var container = document.getElementById("whereButtonsGo");
for (var i = 0; i < items.length; i++) {
var button = document.createElement("button");
button.type = 'button';
button.value = items[i];
button.innerText = items[i];
button.id = 'button'+items[i];
button.onclick = doThing;
container.append(button)
}
}
function doThing() {
console.log('click of ' + this.id);
}
makeButtons();
Note that you don't need to pass the id in the function call for the event - the button that was clicked will be available as this.
Here is a fiddle.
I'm dynamically creating 3 buttons. How can I pass an argument tohandlerX?
So basically I want the values in the category Array to be passed on to the handlerX eventListener.
Example:
When myBtn1 is clicked, I want the alert to be "fur_",
When myBtn3 is clicked, I want the alert to be "fas_"
var btns = '';
var category = ["fur_", "fts_", "fas_"];
for (i = 1; i < category.length; i++) {
btns += '<button type="button" class=' + category[i] + ' id= "myBtn' + i + '">.....</button>';
}
var div = document.getElementById('div');
div.innerHTML = btns;
var handlerX = function () {
alert('Clicked'); //get value from the 'category' Array
};
var buttons = div.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', handlerX, false);
}
The answers given so far are good and should solve your problem. Just thought I'd add this one because I think it's a solution more in line with what you were asking for: Make your handlerX return a function like so:
var handlerX = function (param) {
return function() {alert(param);};
};
var buttons = div.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', handlerX(category[i]), false);
}
Edit: Here's a Fiddle
If you're willing to extract it from the class attribute, then:
var handlerX = function () {
alert(this.getAttribute('class'));
};
Or you better associate it with some data- attribute. For example:
for (i = 1; i < category.length; i++) {
btns += '<button type="button" data-category="' + category[i] + '" class=' + category[i] + ' id= "myBtn' + i + '">.....</button>';
}
Then:
var handlerX = function () {
alert(this.getAttribute('data-category'));
};
See Fiddle
EDIT:
then i would reccomend adding an attibute: data-category="fur_" for example, and access that from your event handler:
this.getAttribute('data-category')
in hadlerX there is a "this" that is the element that was clicked. You can access its getAttribute("class") to get the class EDIT:this, not self
var fragment = document.createDocumentFragment(),
categories = ["fur_", "fts_", "fas_"],
btn;
function onClickBtn() {
alert(this.getAttribute("data-category"));
}
for (var i = 0; i < categories.length; i++) {
btn = document.createElement("button");
btn.id = "myBtn" + string(i);
btn.setAttribute("data-category", category[i]);
btn.addEventListener("click", onClickBtn);
fragment.appendChild(btn);
}
var div = document.getElementById('div');
div.appendChild(fragment);
I have a list when click on the button of each record in the list I delete it.
I use this HTML5 on Anroaid by PhoneGap wrapper, on a computer the Record is deleted, on Android not! What could be the reason?
Here my code:
My list:
<ul id="MyList" data-role="listview" data-divider-theme="b" data-inset="true">
</ul>
I fill the list with this function:
function FillBookMarkListByStation() {
var ul = $("#MyList");
for (var i = 0; i < Local.length; i++) {
var newLI = document.createElement("LI");
ul.appendChild(newLI);
newLI.innerHTML = '<a href="xxx.html" onclick="x();" > <p>...</p>' +
'<a id="btnClear' + i + '" data-role="button" data-icon="delete" data-theme="b"></a>
</a>';
$("#btnClear" + i).click(function () { Clear(ul, newLI); });
}
$('ul').listview('refresh');
}
Function clear:
function Clear(List, Delete) {
//Remove from the list
List.removeChild(Delete);
//Remove from the local storage
for (var i = 0; i < Local.length; i++) {
//Checking is variable for check the data in the item
if (Local[i].Checking == Delete.dataset.checking) {
if (Local.length == 1)
Local= [];
else {
for (var j = i; j < Local.length - 1; j++) {
Local[i] = Local[i] + 1;
}
Local.length--;
}
Local["loc"] = JSON.stringify(Local);
}
}
}
On computer works fine,
on Android is delete from the screen but not from loacl storage,
on presentation of the list is still showing the deleted item.
Problem solved, android will not know the dataset and therefore is stuck ..