JavaScript HTML DOM - Retrieve element ID outside for loop - javascript

I need to create checkbox for each element in data.contents. So far I have created checkboxes using createElemnt(), set ID as their respective title and calling myFunction() during onClick. Now I am trying to retrieve ID when the checkbox is clicked using getElementbyID(b.title) and ended up with an error "b is not defined" which is obvious because I am trying to access b.title outside for loop.
I can't place myFunction() inside for loop because getElementById(b.title) is giving last checkbox's ID for all checkboxes if i do onClick which is also obvious because that's the last iteration's (b.title) of for loop.
My purpose is to retrieve ID(which was dynamically set inside for loop) of a checkbox during onClick from outside for loop. Any help would be much appreciated.
data.contents.forEach(b => {
const btn = document.createElement('input')
btn.type = 'checkbox'
btn.setAttribute("id", b.title)
btn.setAttribute("onClick", "myFunction();");
var t = document.createTextNode(b.title);
mydiv.appendChild(btn);
mydiv.appendChild(t);
});
window.myFunction = function() {
var checkBox = document.getElementById(b.title);
console.log(b.title)
}
HTML
<div id="mydiv">
</div>

Rather than using setAttribute('onClick', consider assigning directly to the onclick property - that will allow you to use the b in that iteration in the handler's closure. There's also no need to create a global function, and you can simply assign to the id property of btn rather than using setAttribute:
data.contents.forEach(b => {
const btn = document.createElement('input')
btn.type = 'checkbox'
btn.id = b.title;
btn.onclick = () => {
console.log(b.title);
// do stuff with button and b
};
var t = document.createTextNode(b.title);
mydiv.appendChild(btn);
mydiv.appendChild(t);
});
If the only reason you were setting the ID was to be able to select it again with getElementById later, you can omit that entirely, due to the closure.

Try this,
data.contents.forEach(b => {
const btn = document.createElement('input')
btn.type = 'checkbox'
btn.setAttribute("id",b.title);
btn.setAttribute("data",b.title);
btn.setAttribute("onClick","myFunction('"+b.title+"');");
var t = document.createTextNode(b.title);
mydiv.appendChild(btn);
mydiv.appendChild(t);
});
window.myFunction = function(id) {
var checkBox = document.getElementById(id);
console.log(id);
}

Related

Why isn't my function related to an onclick workin?

I'm studding HTML, CSS and JS and while I was creating an exercise, I was forced to stop due to an error. I created an button dynamically, setted an onclick to it and then created a function with that onclick. The problem is that the function isn't working, at leats it doesn't made anything till now.
let formularys = document.querySelector('section#formulary')
let slct = document.createElement('select')
let opts = document.createElement('option')
let optp = document.createElement('option')
let fbtn = document.querySelector('input#formularybtn')
let nbtn = document.createElement('input')
let br = document.createElement('br')
slct.id = 'pors'
slct.size = '2'
opts.value = 'rsite'
opts.innerHTML = 'Rate site'
optp.value = 'rp'
optp.innerHTML = 'Rate products'
nbtn.setAttribute('type', 'button')
nbtn.setAttribute('value', 'Next')
nbtn.setAttribute('onclick', 'nbutton')
function nbutton(){
console.log('Next working')
/*if(slct.selectedIndex == 1){
console.log('Valid rate choose')
}*/`enter code here`
}
instead of using setAttribute you can just do
nbtn.onclick = nbutton
in javascript, onclick isn't a string but a function.
The problem is, you are not appending your html code generated from JavaScript to DOM. You can either append to main DOM like below
document.body.appendChild(nbtn);
document.body.appendChild(optp);
or you can append them to some parent div by first getting div id
document.getElementById("divId").appendChild(nbtn);
where divId is id of your div where you want to add this html.
Also you should assign event listener in correct way as suggested by Tony and Rashed Rahat.
Try:
element.onclick = function() { alert('onclick requested'); };

how to remove selected list element, instead of removing only top li tag

I'm trying to remove specific li elements, based off of which one has the x button clicked. Currently I'm having an error
"bZMQWNZvyQeA:42 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'."
I am aware that this could mean that the paramater is null, but this dosn't make any sense to me. Chrome dev tools show that the onClick attribute is correctly exectuing removeItem, and passing in the idName as a parameter. How is this not working?
var note = 0;
function saveInfo() {
var idName = "note" + note;
//assign text from input box to var text, and store in local storage
var input = document.getElementById('input').value;
var text = localStorage.setItem(note, input);
var list = document.createElement("li");
var node = document.createTextNode(input);
var removeBtn = document.createElement("button");
list.setAttribute("id", idName);
removeBtn.setAttribute("onClick", `removeItem(${idName})`);
removeBtn.innerHTML = "X";
list.appendChild(node);
list.appendChild(removeBtn);
document.getElementById("output").appendChild(list);
note += 1;
}
function removeItem(name) {
var parent = document.getElementById("output");
var child = document.getElementById(name);
parent.removeChild(child);
}
In my comment, I suggested that you listen to click event bubbling from the removeBtn. In this case, all you need is to remove the onclick attribute assignment logic from your code, and instead give your removeButton an identifiable property, such as a class. Lets give it a class of delete-button:
var removeBtn = document.createElement("button");
removeBtn.classList.add('delete-button');
removeBtn.type = 'button';
removeBtn.innerHTML = 'X';
Then, you can listen to the click event at the level of #output, which is guaranteed to be present at runtime. When the event is fired, you simply check if the event target has the identifiable property, e.g. the remove-button class in our case:
output.addEventListener('click', function(e) {
// GUARD: Do nothing if click event does not originate from delete button
if (!e.target.matches('.remove-button')) {
return;
}
// Delete parent node
e.target.closest('li').remove();
});
If the click event did not originate from the remove button, we simply return and don't do anything else. Otherwise, we know that the button has been clicked, and we can then use Element.closest(), i.e. .closest('li') to retrieve the closest <li> parent node and delete it.
If you absolutely have to support IE11 (which in turn, does not support Element.closest()), you can also use Node.parentNode to access and delete the <li> element, assuming that your remove button is a direct child of the <li> element:
// Delete parent node
e.target.parentNode.remove();
See proof-of-concept below:
var rows = 10;
var output = document.getElementById('output');
for (var i = 0; i < rows; i++) {
var list = document.createElement('li');
var node = document.createTextNode('Testing. Row #' + i);
var removeBtn = document.createElement("button");
removeBtn.classList.add('remove-button');
removeBtn.type = 'button';
removeBtn.innerHTML = 'X';
list.appendChild(node);
list.appendChild(removeBtn);
output.appendChild(list);
}
output.addEventListener('click', function(e) {
// GUARD: Do nothing if click event does not originate from delete button
if (!e.target.matches('.remove-button')) {
return;
}
e.target.closest('li').remove();
});
<ul id="output"></ul>
The issue is that you have missing quotes around the id that you pass to removeItem:
removeBtn.setAttribute("onClick", `removeItem(${idName})`);
This should be:
removeBtn.setAttribute("onClick", `removeItem('${idName}')`);
Better pattern
It is better practice to bind the click handler without relying on string evaluation of code, and without needing to create dynamic id attribute values:
removeBtn.addEventListener("click", () => removeItem(list));
And then the function removeItem should expect the node itself, not the id:
function removeItem(child) {
child.parentNode.removeChild(child);
}
You can remove the following code:
var idName = "note" + note;
list.setAttribute("id", idName);

How can I solve this Null property on Javascript?

I'm trying to put a delete button on each li using JavaScript and to make an event handler that runs when a button is clicked that removes the li. However when I try to add the handler, I get:
Cannot read property 'addEventListener' of null
I think this is because I am referencing a class that not exist before run the function createbtn. So How can I solve this?
The Code:
I set the variables, put querySelector to buttons because I testing how to do it:
var button = document.getElementById("enter");
var input = document.getElementById("userinput");
var ul = document.querySelector("ul");
var list = document.querySelectorAll ("li");
var buttons = document.querySelector (".btn-danger");
var li = document.createElement("li")
How I create the button:
function createbtn() {
for (var i = 0; i < list.length; i++) {
var btn = document.createElement("button");
btn.appendChild(document.createTextNode("Delete"));
btn.classList.add("btn", "btn-danger","btn-sm");
list[i].appendChild(btn);
}
}
The function I try to run:
function liDel(){
li.parentNode.removeChild(li);
}
buttons.addEventListener("click", liDel);
This is my fiddle to see all the code.
The reason why you are getting the null error is because;
You have assigned the variable buttons to a node which doesn't exist yet. (Note that the button is created after the page has been loaded, which means .btn-danger hasn't yet been created at that time).
According to MDN the querySelector method does the the ff:
The Document method querySelector() returns the first Element within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
Based on the code you have in the fiddle, here is a guide to achieve the desired results.
First of all, get rid of the global li variable on line 6.
The reason is that if you create a new li from the input, it will render on the same line because it's still referencing the same element node (I'm sure you've realized that)
then in your createListElement function, do the ff
function createListElement() {
var li = document.createElement('li');
li.appendChild(document.createTextNode(input.value));
var btn = document.createElement("button");
btn.appendChild(document.createTextNode("Delete"));
btn.classList.add("btn", "btn-danger","btn-sm");
btn.addEventListener('click', function(e){
if(!e) e = window.event;
try{
ul.removeChild(this.parentNode)
}catch(err){
alert(err.message)
}
})
li.appendChild(btn)
ul.appendChild(li);
input.value = "";
}
Then when you create the buttons, you have to attach the event listener function to it. So you do the ff in your createbtn function:
// To create a button
function createbtn() {
for (var i = 0; i < list.length; i++) {
var btn = document.createElement("button");
btn.appendChild(document.createTextNode("Delete"));
btn.classList.add("btn", "btn-danger","btn-sm");
btn.addEventListener('click', function(e){
if(!e) e = window.event;
try{
ul.removeChild(this.parentNode)
}catch(err){
alert(err.message)
}
})
list[i].appendChild(btn);
}
}
anyways, there are more efficient ways to do this. But this is a quick workable model based on the code in your fiddle
Rather than querying and adding the event to the buttons object
try the chaining inside the document load.
window.onload = function () {
document.querySelector('.btn-danger').addEventListener('click', liDel);
};
The above code should work!
Thanks a lot everybody, I got a solution after reading all your answers:
First I got rid the following:
var buttons = document.querySelector (".btn-danger");
var li = document.createElement("li")
Then create this function for remove the "li"
Using "this" you avoid the error for don't have a reference, because with that you don't care in what kind of element this is, you only now something is there and grab it for anything you need.
function liDel(){
ul.removeChild(this.parentNode);
}
and put this in createBtn for delete the existing "li" in the html:
btn.addEventListener('click', liDel);
then put this on createElement for do the same of the above, but for the new "li" creates with the DOM:
btn.addEventListener('click', liDel);
li.appendChild(btn);
And with that the problems was solved.
Thanks again and you can see how the page works on the fiddle

Delete Button not working properly (DOM)

Ok here is what I was trying to do... Create a delete button along with edit by using DOM while creating a paragraph. But delete button always seems to be deleting first paragraph instead of deleting the corresponding paragraph.. here's my code:
Javascript:
function writePara()
{
var comment = document.getElementById("usrinput").value;
var newParagraph = document.createElement('p');
newParagraph.textContent = comment;
document.getElementById("updateDiv").appendChild(newParagraph);
var button = document.createElement("button");
var Btext=document.createTextNode("EDIT");
button.appendChild(Btext);
document.getElementById("updateDiv").appendChild(button);
button.onclick =
(
function()
{
var edit = prompt("Type to edit", "");
newParagraph.innerHTML = edit;
}
);
var button2 = document.createElement("button");
var Btext2=document.createTextNode("DELETE");
button2.appendChild(Btext2);
document.getElementById("updateDiv").appendChild(button2);
button2.onclick =
(
function ()
{
var items = document.querySelectorAll("#updateDiv p");
if (items.length)
{
var child = items[0];
child.parentNode.removeChild(child);
}
button.parentNode.removeChild(button);
button2.parentNode.removeChild(button2);
}
);
addBr();
}
Any ideas guys?
You already have a reference to the new paragraph in the writePara function and you already used it once in the edit handler, so why don't you use it again in the delete handler?
button2.onclick =
(
function ()
{
newParagraph.parentNode.removeChild(newParagraph);
button.parentNode.removeChild(button);
button2.parentNode.removeChild(button2);
}
);
Here's how it works: http://jsbin.com/nohud/1/edit. Write something in the input and click outside of it a few times to generate some paragraphs.
Edit: The code above utilizes closures. It is important to understand that each time writePara is called, the newParagraph variable points to a new DOM element and each click event handler defined in the same function has access to that specific element in the newParagraph variable. So whenever the edit/delete handlers are called newParagraph is the element with which the associated buttons have been created when writePara has been called.
Here's some code that explains that clearer that I do:
function init() {
var name = "Mozilla"; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert (name); // displayName() uses variable declared in the parent function
}
displayName();
}
init();
It is taken from here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures. More also here: How do JavaScript closures work?.
From there on newParagraph.parentNode is the container containing the new paragraph, so newParagraph.parentNode.removeChild(newParagraph) just removes that specific element from its container.
Its because you are always giving index[0] so that its deleting first paragraph as shown below
var child = items[0];
it should be
newParagraph.parentNode.removeChild(newParagraph);

Change the onClick function to target the edit buttons

I have the following script
var counter = 0;
function appendText(){
var text = document.getElementById('usertext').value;
if ( document.getElementById('usertext').value ){
var div = document.createElement('div');
div.className = 'divex';
var li = document.createElement('li');
li.setAttribute('id', 'list');
div.appendChild(li);
var texty = document.createTextNode(text);
var bigdiv = document.getElementById('addedText');
var editbutton = document.createElement('BUTTON');
editbutton.setAttribute('id', 'button_click');
var buttontext = document.createTextNode('Edit');
editbutton.appendChild(buttontext);
bigdiv.appendChild(li).appendChild(texty);
bigdiv.appendChild(li).appendChild(editbutton);
document.getElementById('button_click').setAttribute('onClick', makeAreaEditable());
document.getElementById('usertext').value = "";
counter++;
}
};
var makeAreaEditable = function(){
alert('Hello world!');
};
I want the makeAreaeditable function to work when the Edit button is pressed(for each of the edit buttons that are appended under the textarea).. In this state, the script, alerts me when i hit the Addtext button.
the following is the html. P.S. i need this in pure javascript, if you can help. thanks
<textarea id="usertext"></textarea>
<button onClick="appendText()">Add text </button>
<div id="addedText" style="float:left">
</div>
instead of:
document.getElementById('button_click').setAttribute('onClick', makeAreaEditable());
you need to do this:
editbutton.onclick = makeAreaEditable;
the function's name goes without brackets unless you want to execute it
instead of obtaining the element from the DOM using document.getElementById('button_click')
you can use the editbutton variable already created. this object is the DOM element you are looking for
SIDE NOTE:
the standard way to do it is to add the onclick property before appending the element

Categories