Building a keyboard and missing onclick event - javascript

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>

Related

Javascript adding li element value to the empty list if it is not existing

I have a problem in Javascript.I am adding new list items to the 'ul' elements and this list is empty at first and I do not want to add same values twice. When I write the if statement I get the exception because my list is empty so the result return null.
How can I fix this this problem?
Thank you in advance...
Html Codes
<input type="text" id="the-filter" placeholder="Search For..." />
<div class="list-container">
<ul id="myList"></ul>
<button id="button">Click</button>
Javascript Codes
let newlist = document.querySelector("#myList");
const li = document.getElementsByClassName('list-group-item');
const button = document.getElementById("button");
const button.addEventListener('click' , listName);
const input = document.getElementById("the-filter");
function listName()
const inputVal = input.value;
for (i = 0; i < li.length; i++) {
if ((li[i].innerHTML.toLocaleLowerCase().includes(inputVal) && inputVal!="") ||
(li[i].innerHTML.toUpperCase().includes(inputVal) && inputVal!="")) {
let newItem = document.createElement("li");
li[i].classList.add("list-group-item");
let textnode = document.createTextNode(li[i].innerHTML.toLocaleLowerCase());
newItem.appendChild(textnode);
if((newlist.children[0].innerHTML.toLocaleLowerCase().includes(inputVal))){
newlist.insertBefore(newItem, newlist.childNodes[0]);
}
}
}
}
If I understood the task correct, you need to add items to the list by button click.
If same item exists (case insensitive), then nothing happens.
const list = document.querySelector("#myList");
const button = document.getElementById("button");
button.addEventListener("click", listName);
const input = document.getElementById("the-filter");
function listName() {
const inputVal = input.value;
const [...lis] = document.getElementsByClassName("list-group-item");
const same = lis.find((el) => el.textContent.toLowerCase() === inputVal.toLowerCase());
if (same) {
return;
}
let newItem = document.createElement("li");
newItem.classList.add("list-group-item");
newItem.textContent = inputVal;
list.appendChild(newItem)
}
<input type="text" id="the-filter" placeholder="Search For..." />
<div class="list-container">
<ul id="myList"></ul>
<button id="button">Click</button>
</div>
You're on the right track with event listeners and element creation, but your original code didn't quite seem to match your stated goal.
Here's a solution you might find useful, with some explanatory comments:
// Identifies some DOM elements
const
input = document.getElementById("my-input"),
newList = document.getElementById("my-list"),
items = document.getElementsByClassName('list-group-item'),
button = document.getElementById("my-button");
// Focuses input, and calls addItem on button-click
input.focus();
button.addEventListener('click', addItem);
// Defines the listener function
function addItem(){
// Trims whitespace and sets string to lowerCase
const inputTrimmedLower = input.value.trim().toLocaleLowerCase();
// Clears and refocuses input
input.value = "";
input.focus();
// Ignores empty input
if (!inputTrimmedLower) { return; }
// Ignores value if a list item matches it
for (const li of items) {
const liTrimmedLower = li.textContent.trim().toLocaleLowerCase();
if (liTrimmedLower === inputTrimmedLower) {
console.log(`${inputTrimmedLower} is already listed`);
return;
}
}
// If we got this far, we want to add the new item
let newItem = document.createElement("li");
newItem.classList.add("list-group-item");
newItem.append(inputTrimmedLower); // Keeps lowerCase, as your original code
newList.prepend(newItem); // More modern method than `insertBefore()`
}
<input id="my-input" />
<ul id="my-list"></ul>
<button id="my-button">Click</button>

values inside input box become null when i add new input box with javascript

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>

How can I get my JavaScript delete function to fire more than once?

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>

Search data from specific DOM part

I have a search input field, and I want to get the user's input dynamically and update a list contained in a <div>
I'm setting up a search solution for a catalogue where you can display a list of items.
Right now all the items are shown at once.
the html
<input type="search" id="search-field" placeholder="Search...">
</div>
<div class="container">
<p id="guide">Select a category from the menu</p>
<ul id="product-list">
</ul>
<script src="./preload.js"></script>
</div>
the js code
var fileList = document.getElementById('product-list');
var title = document.getElementById('guide');
title.innerHTML = `You are looking at the ${products} catalog.`;
for(var i = 0; i < thumbFiles.length; i++){
var pathName = thumbFiles[i];
var finalName = pathName.split('/').pop();
fileList.innerHTML = document.getElementById('product-list').innerHTML +
`<li class="product-container">
<div class="new-thumb">
<a href="${(items[i])}" id='product-placeholder' target="_blank">
<img src="${(thumbFiles[i])}" alt="thumb" class="thumbnail">
</a>
<h4>${(path.parse(finalName).name)}</h4>
</li>`
};
}
I expect to update the <ul> with the search input text matching element from the catalogue.
You can attach an oninput listener to your element, like this :
In your js script
const myInput = document.querySelector(‘#search-field’);
const fileList = document.querySelector('#product-list');
myInput.addEventListener(‘input’, () => {
// Don’t forget to clean your results on each new input
fileList.innerHTML = ‘’;
for(const index in thumbFiles) {
const pathName = thumbFiles[index];
const finalName = pathName.split('/').pop();
// Here is the function that will check if your current item match user input
if (match(finalName, myInput.value)) {
fileList.innerHTML += [your stuff]
}
}
});
this will trigger a function each time your input is changing value. Here, it calls our arrow function in which we pack all the actions we want to execute when user input some text.
EDIT
Forgot to compare with current input. You can get input value by calling myInput.value
An example of what match() can be :
function match(name, userInput) {
return name.includes(userInput);
}
This will just check if the finalName contains the user input.

Why aren't the user inputs being put into the empty array?

I'm trying to write a program so that once the user clicks the 'Add!' button, the string that they typed will be added to an initially empty array in an object, and then that updated array will be displayed back on the HTML page. However, when I checked what the value of the items array was when I typed something in, it still appeared to be null. I'm fairly certain that the addItem function is fine, is the problem in the updateList function?
HTML CODE:
<!DOCTYPE html>
<html>
<head>
<title>Homework 5</title>
<!--<link rel="stylesheet" type="text/css" href="index.css">-->
<script src="toDoList.js"></script>
</head>
<body>
<h1>Homework 5: JS Objects & HTML DOM</h1>
<div id="input">
<input id="userInput" type="text" placeholder="Type a word">
<button id="submit">Add</button>
</div>
<div id="output"></div>
<h1>Design</h1>
<h1>Challenges</h1>
</body>
</html>
JAVASCRIPT CODE:
var button = document.getElementById("submit");
var toDoList = {
items: [],
add: addItem,
update: updateList
};
function addItem(string) {
toDoList.items.push(string);
}
function updateList() {
var output = document.getElementById("output");
output.innerHTML = toDoList.items;
}
function getInput() {
var input = document.getElementById("userInput").value;
toDoList.add(input);
toDoList.update();
//clearing the text field for next use
document.getElementById("userInput").innerHTML = "";
}
button.addEventListener('click', getInput());
The second argument provided to addEventListener needs to be a function. If you put a function invocation there, that function is executed immediately, with its return value assigned as the handler. But if the return value isn't a function, the event listener doesn't work.
In your case, you just want getInput to be run when the button is clicked - getInput is not a higher-order function, so just pass the function itself, rather than invoking it:
button.addEventListener('click', getInput);
Like this
var button = document.getElementById("submit");
var toDoList = {
items: [],
add: addItem,
update: updateList
};
function addItem(string) {
toDoList.items.push(string);
}
function updateList() {
var output = document.getElementById("output");
output.innerHTML = toDoList.items;
}
function getInput() {
var input = document.getElementById("userInput").value;
toDoList.add(input);
toDoList.update();
//clearing the text field for next use
document.getElementById("userInput").innerHTML = "";
}
button.addEventListener('click', getInput);
<h1>Homework 5: JS Objects & HTML DOM</h1>
<div id="input">
<input id="userInput" type="text" placeholder="Type a word">
<button id="submit">Add</button>
</div>
<div id="output"></div>
<h1>Design</h1>
<h1>Challenges</h1>
You should not invoke or execute the function in addEventListener. Invoking function causes the function to execute immediately not when the event (click) happens. So remove parenthesis after the function name.
Change button.addEventListener('click', getInput());
To
button.addEventListener('click', getInput);
var button = document.getElementById("submit");
var toDoList = {
items: [],
add: addItem,
update: updateList
};
function addItem(string) {
toDoList.items.push(string);
}
function updateList() {
var output = document.getElementById("output");
output.innerHTML = toDoList.items;
}
function getInput() {
var input = document.getElementById("userInput").value;
toDoList.add(input);
toDoList.update();
//clearing the text field for next use
document.getElementById("userInput").innerHTML = "";
}
button.addEventListener('click', getInput);
<h1>Homework 5: JS Objects & HTML DOM</h1>
<div id="input">
<input id="userInput" type="text" placeholder="Type a word">
<button id="submit">Add</button>
</div>
<div id="output"></div>
<h1>Design</h1>
<h1>Challenges</h1>
I think, you have to loop through your array. to get the content:
var x = ['apple','banana','orange'];
var output = "";
for (i=0;i<x.length;i++) {
output += x[i];
}
alert(output); //--> outputs applebananaorange
alert(x.items); //--> undefined (whats your case)

Categories