I am dynamically adding a new textbox on a click event.
I am not able to get the value of each textbox, any help would be appreciated, thanks.
In my html page:
<div id="job-container">
<p>
<label>Job Property</label><br>
<input name="JobProperty[1][job_property]" />
</p>
</div>
<a href="javascript:" id="add-new-jobproperty"
onclick="createJobProperty()">Add new job</a>
In my Javascript code
let i = 2;
function createJobProperty() {
let template = `
<p>
<label>Job Property</label><br>
<input name="JobProperty[${i}][job_property]">
</p>`;
let container = document.getElementById('job-container');
let div = document.createElement('div');
div.innerHTML = template;
container.appendChild(div);
definedLength=i;
i++;
}
Try to add a class to inputs and then get them by className (since by tag name, you may have other inputs), and iterate through each of them to get values.
<input class='my-input' name="JobProperty[${i}][job_property]"> use this code to add a new input
var inputs = document.getElementsByClassName('my-input');
Array.from(inputs).forEach(function(element) {
console.log(element.value);
});
//or
Array.prototype.forEach.call(inputs, function(element) {
console.log(element.value);
});
You could try:
let i = 2;
function createJobProperty() {
let template = `
<p>
<label>Job Property</label><br>
<input name="JobProperty[` + i + `][job_property]">
</p>`;
let container = document.getElementById('job-container');
let div = document.createElement('div');
div.innerHTML = template;
container.appendChild(div);
let input = container.querySelector("input[name='JobProperty["+i+"][job_property]']");
// do what ever you want with the input
definedLength=i;
i++;
}
Related
I'm building a virtual keyboard with vanillla javascript but don't know where to add the onclick event listener to the buttons or how to grab them. I have a printKeys function that loops thru the array and prints them onload, and I have an unfinished typeKeys function where I'm trying to grab the innerhtml and print it to the input field.
HTML
</head>
<body onload="printKeys()">
<div class="text">
<input type="text" class="your-text" id="input" placeholder="Your text here.."></input>
<button class="copy-btn">Copy</button>
</div>
<div class="keyboard" id="keyboard"></div>
<script src="index.js"></script>
</body>
</html>
Javascript
const alphaKeys = ["a","b","c"];
const numKeys = "1234567890";
const keyboard = document.getElementById("keyboard");
// render keyboard
function printKeys() {
for (let i = 0; i < alphaKeys.length; i++) {
let keys = document.createElement("button");
keys.innerHTML = alphaKeys[i];
//add onclick function to button
keyboard.appendChild(keys);
}
}
//onClick event, add text in text field
const input = document.getElementById('input')
function typeKeys() {
console.log("clicked")
//grab input and replace with button innerhtml
}
Instead of adding the event handler to each button, you can apply it to the parent (keyboard) then just use the event's target to get the specific button. I also added the character to a data-attribute instead of the innerHTML.
const alphaKeys = ["a","b","c"];
const numKeys = "1234567890";
const keyboard = document.querySelector(".keyboard");
// render keyboard
function printKeys() {
for (let i = 0; i < alphaKeys.length; i++) {
let keys = document.createElement("button");
keys.innerHTML = alphaKeys[i];
keys.setAttribute("data-character",alphaKeys[i]);
keyboard.appendChild(keys);
}
}
//onClick event, add text in text field
const input = document.getElementById('input')
function typeKeys(character) {
input.value += character;
}
keyboard.addEventListener("click",function(e){
let target = e.target;
if(target.getAttribute("data-character")){
typeKeys(target.getAttribute("data-character"))
}
});
printKeys();
<div class="text">
<input type="text" class="your-text" id="input" placeholder="Your text here..">
<button class="copy-btn">Copy</button>
</div>
<div class="keyboard" id="keyboard"></div>
when i add new input box with javascript function, previous input boxes become empty. here is the code:
<div id="field">
<input type="text">
</div>
<div id="error"></div>
<button onclick="Add()">add</button>
<script>
let i=0;
const Add=()=>{
i++
if(i<5)
document.getElementById('field').innerHTML+=`<input type="text" id="value${i}">`
else
document.getElementById('error').innerHTML='Error: Field cant be more then 5'
}
</script>
what can I do to NOT change input values of input box on adding new input box with above codes.
You are overwriting the entire HTML content of ID field,
let i = 0;
const Add = () => {
i++
if (i < 5) {
const input = document.createElement('input');
document.getElementById('field').appendChild(input);
} else
document.getElementById('error').innerHTML = 'Error: Field cant be more then 5'
}
<div id="field">
<input type="text">
</div>
<div id="error"></div>
<button onclick="Add()">add</button>
One way of doing it, keeping in mind separation of concerns and avoiding the creation of unnecessary global variables could be:
#error {
display: none;
}
<div id="field">
<input type="text">
</div>
<div id="error"></div>
<button onclick="Add()">add</button>
<script>
const Add = () => {
const inputContainer = document.querySelector('#field'); // this variable is not strictly necessary but I think it makes the code more readable
const inputNotification = document.querySelector('#error'); // this variable is not strictly necessary but I think it makes the code more readable
const inputCount = inputContainer.querySelectorAll('input[type=text]').length // count how many input elements of type text are already inside the `field` div
if (inputCount < 5) {
const txtInput = document.createElement('input');
txtInput.setAttribute('type', 'text');
txtInput.setAttribute('id', `value${inputCount}`);
inputContainer.append(txtInput);
} else {
inputNotification.innerText = 'Error: Field can\'t be more than 5';
inputNotification.style.display = 'block'
event.target.setAttribute('disabled', true); // optionally, you can disable the add button if you reached the maximum number of input fields
}
}
</script>
You could use Document.createElement() and element.appendChild() so that you do not alter the HTML of the div#field :
<div id="field">
<input type="text">
</div>
<div id="error"></div>
<button onclick="Add()">add</button>
<script>
let i=0;
const Add=()=>{
i++
if(i<5) {
let input = document.createElement("input");
input.type = "text";
input.id = "value" + i;
let button = document.createElement("button");
button.innerText = "delete";
button.onclick = function(){
input.remove(); //remove text input
this.remove(); //remove this delete button
i--; //decrement i
};
document.getElementById('field').appendChild(input);
document.getElementById('field').appendChild(button);
} else {
document.getElementById('error').innerHTML='Error: Field cant be more then 5';
}
}
</script>
I'm building a todo app and I use a function to create a list item entered by the user.
There is an event listener added to the output section to listen for a delete button click for each item displayed. My problem is that the delete button is only working for one item and then it stops working.
In the console, it appears that the function is actually called every time I press the button, but the functionality only works for one click. Do I need to add all the list items into an array perhaps?
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
function listItem(todo) {
createListItem = document.createElement('li');
createListItem.innerText = todo.value;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
}
output.addEventListener('click', deleteFunc);
function deleteFunc() {
console.log('function called');
createListItem.remove();
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>
You need to delegate and use relative addressing because your code only removes the LAST added LI
The variable createListItem pollutes the global scope. Add the keyword var or let in the listItem function too
function deleteFunc(e) {
console.log('function called');
const tgt = e.target;
if (e.target.innerText==="delete") tgt.closest("li").remove()
}
Added benefit from this delegation is that adding the functionality to the "check" button is just
if (e.target.innerText==="check") ...
I would recommend to use a class and testing
if (e.target.classList.contains("delete")
instead of the innerText - especially if you want to change language of the button
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
function listItem(todo) {
let createListItem = document.createElement('li'); // use let or var here
createListItem.innerText = todo.value;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
}
output.addEventListener('click', deleteFunc);
function deleteFunc(e) {
console.log('function called');
const tgt = e.target;
if (e.target.innerText==="delete") tgt.closest("li").remove()
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>
Your createListItem variable is a global that gets set to the most-recently appended item, so the delete function will always delete the most-recent item. Once an element el has already been removed from the DOM tree, el.remove() is a no-op, so it only works once.
To fix, you can either use event delegation as in #mplungjan's answer or assign a unique id to each list item and pass that as a parameter to the function to determine what to delete.
Example of the second approach:
<ul>
<li id="item-0"></li>
<li id="item-1"></li>
<li id="item-2"></li>
</ul>
const deleteById = id => document.querySelector(`#item-${id}`).remove()
First though:
I think os because you are targeting the "ul" instead the "li" elements. Try adding the event listeners to each "li" element with a querySelectorAll() instead of targeting "output" directly.
When you add another item, you call listItem() which sets a new value to createListItem. This means, when you call deleteFunc(), createListItem.remove(); is only executed on the last element you have added. If this item was already removed, your out of luck as well.
function deleteFunc(e) {
console.log('function called');
const { target } = e; // get target
target.closest("li").remove()
}
Remove the listener on output. (This line: output.addEventListener('click', deleteFunc);)
And add the listener to each deleteBtn:
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
deleteBtn.addEventListener('click', deleteFunc);
This might help you:
https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
Find the solution.
const todo = document.getElementById('todo');
const enter = document.getElementById('enter');
const output = document.getElementById('output');
enter.addEventListener('click', () => {
listItem(todo);
});
let createListItem;
var deleteBtn;
let checkBtn;
var index = 0;
function listItem(todo) {
createListItem = document.createElement('li');
createListItem.innerText = todo.value;
createListItem.id= 'li' + index;
todo.value = '';
output.appendChild(createListItem);
checkBtn = document.createElement('button');
deleteBtn = document.createElement('button');
checkBtn.innerText = 'check';
deleteBtn.innerText = 'delete';
deleteBtn.id = 'btn' + index;
createListItem.append(checkBtn);
createListItem.append(deleteBtn);
checkBtn.classList.add('checkBtn');
deleteBtn.classList.add('deleteBtn');
deleteBtn.onclick = function() { deleteFunc(this); };
index += 1;
}
//output.addEventListener('click', deleteFunc);
function deleteFunc(e) {
var rowId = e.id.replace('btn','');
var row = document.getElementById('li'+rowId);
console.log('function called' + rowId);
//alert(rowId);
if (row != null) {
row.remove();
}
//console.log('function called');
//createListItem.remove();
}
<section class="controls">
<div>
<label for="todo">Enter a to-do</label>
<input type="text" name="todo" id="todo">
</div>
<span>
<button id="enter" class = "enter"><i class="fas fa-paper-plane"></i></button>
</span>
</section>
<section>
<ul id="output" class="output">
</ul>
</section>
const input = document.createElement('input');
input.type = 'checkbox';
I can create a checkbox using the above code. However, i need to wrap it in div class so that my css can format it. Can this be done?
<div class="kt-checkbox-list">
<label class="kt-checkbox">
<input type='checkbox'/>
<span></span>
</label>
</div>
You can use something like this:
<label id="an-id" class="kt-checkbox">
and then
document.getElementById("an-id").innerHTML = "<input type='checkbox' />";
In pure JS it's fairly simple, just create div and label tags with the appropriate classes:
const div = document.createElement("div");
div.class = "kt-checkbox-list";
const label = document.createElement("label");
div.appendChild(label);
label.class = "kt-checkbox";
const input = document.createElement("input");
label.appendChild(input);
input.type = "checkbox";
document.body.appendChild(div);
You can simply create a HTML string, and then add this to your document's body using document.body.innerHTML like so:
const html_str = `
<div class="kt-checkbox-list">
<label class="kt-checkbox">
<input type='checkbox'/>
<span></span>
</label>
</div>`;
document.body.innerHTML = html_str;
(Note: I have used template literals (`) for the string as it allows me to write the HTML on multiple lines. A normal string encapsulated in double or single quotes will also work, but you need to be more mindful of how you format it within your JS)
const input = document.createElement('input');
const list = document.createElement('div');
const label = document.createElement('label');
const span = document.createElement('span');
list.classList.add('kt-checkbox');
label.classList.add('kt-checkbox');
label.htmlFor= 'abc';
input.id = 'abc';
input.setAttribute('type', 'checkbox');
label.append(input, span)
list.append(label);
document.body.append(list);
OR
function createElement(type, attr, ...css) {
const elem = document.createElement(type);
attr && attr.map(obj => Object.keys(obj).map(key => elem.setAttribute(key, obj[key])));
elem.classList.add(...css);
return elem;
}
const input = createElement('input', [{'type': 'checkbox'}, {'id': 'abc'}]);
const list = createElement('div', null, 'kt-checkbox-list');
const label = createElement('label', [{'htmlFor': 'abc'}], 'kt-checkbox');
const span = document.createElement('span');
label.append(input, span)
list.append(label);
document.body.append(list);
I have a problem with array in Javascript. I have a button that pushes an item to an array. After I click the button, the for loop which is intended to display a list of items in a div isn't executed.
Below is the code:
HTML:
<input type="text" id="tb">
<button id="btn">Submit</button>
<div id="list"></div>
Javascript:
var list = [];
btn.onclick = function(){
list.push(document.getElementById("tb").value);
}
for (var i in list){
document.getElementById("list").innerHTML = "<p>"+list[i]+"</p>"
}
Are there any solutions for this such that after I click the button, the div updates the list of items, much like a to-do list?
You can just append the html to the list when clicking the button - see demo below:
btn.onclick = function() {
document.getElementById("list").innerHTML +=
"<p>" + document.getElementById("tb").value + "</p>"
}
<input type="text" id="tb">
<button id="btn">Submit</button>
<div id="list"></div>
If you want to use the array as a data store as you say in the comments to this answer, you may do something like given in the snippet below:
var list = [];
btn.onclick = function() {
// add to the list
list.push(document.getElementById("tb").value);
// TODO: save to local storage if needed
// reset the list
document.getElementById("list").innerHTML = '';
// display the list
for (var i in list) {
document.getElementById("list").innerHTML += "<p>" + list[i] + "</p>";
}
}
<input type="text" id="tb">
<button id="btn">Submit</button>
<div id="list"></div>
i'd do:
var list = [];
btn.onclick = function(){
list.push(document.getElementById("tb").value);
for (var i in list){
document.getElementById("list").innerHTML = "<p>"+list[i]+"</p>"
}
}
As you can see the for loop must be triggered in its execution so i moved it inside the function
Just append a new <p> element to the list instead of using innerHTML. Also, you should bind event listeners using addEventListener:
var list = document.getElementById('list');
var input = document.getElementById('tb');
document.getElementById('btn').addEventListener('click', function () {
var p = document.createElement('p');
p.textContent = input.value;
list.appendChild(p);
});
<input type="text" id="tb">
<button id="btn">Submit</button>
<div id="list"></div>
var list = [];
var btn = document.getElementById("btn");
btn.onclick = function(){
var tb = document.getElementById("tb").value;
//list.push(tb);
document.getElementById("list").innerHTML += "<p>"+tb+"</p>"
}
<input type="text" id="tb">
<button id="btn">Submit</button>
<div id="list"></div>