i have created a webside, there I have a div element with the id "buttons".
Below you see how i create buttons with the alphabet on them. Everything fine and it works.
But now I have to get access to only one button/one letter of the 26 buttons.. so that I can change the opacity of one button. (i just can handle it that all of them get more transparent!)
maybe somebody can help me -thanks!
var buttons = function () {
myButtons = document.getElementById("buttons");
letters = document.createElement('ul');
for (var i = 0; i < alphabet.length; i++) {
letters.id = 'alphabet';
list = document.createElement('li');
list.id = 'letter';
list.innerHTML = alphabet[i];
check();
myButtons.appendChild(letters);
letters.appendChild(list);
}
}
You can set the id of your list elements with list.id='letter' + i.
Then you can access each button with document.getElementById('letter<i>') - for the first one document.getElementById('letter0') and so on.
You should move letters.id = 'alphabet'; outside of your for-loop because you don't have to set the id in every loop.
I updated your codes, You can just see the example.
Example:
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /*Just Defined for this snipped test*/
var buttons = function (ID) {
var ul = document.createElement('ul');
ul.id = 'alphabet';
for (var i = 0; i < alphabet.length; i++) {
li = document.createElement('li');
li.id = 'letter'+i; /*id="letter0",id="letter1",...*/
li.innerHTML = alphabet[i];
/*check();*/
ul.appendChild(li);
}
ID.appendChild(ul);
}
var myButtons = document.getElementById("buttons");
buttons(myButtons); /*Called the function and pass the Object for append*/
<div id="buttons"></div>
Generated HTML code from above snippet:
<div id="buttons">
<ul id="alphabet">
<li id="letter0">A</li>
<li id="letter1">B</li>
<li id="letter2">C</li>
.......................
<li id="letter25">Z</li>
</ul>
</div>
So, you can use different CSS for every ID's. Or you can change CSS from button() function by updating your code.
You may NOT use same ID to different elements (You had created more then one element and gave it same ID).
Here another method to solve the problem you had shown. (without using "id" field), Just because you already have very good answers using the "id" property properly.
<!doctype html>
<html>
<head>
<title>
Test
</title>
<script type='text/javascript'>
'use strict';
var m_Buttons = [];
function Init() {
var myButtons = document.getElementById("buttons");
var alphabet = ['a','b','c','d','e','f','g']; //go on with letters yourself
var ul = document.createElement('ul');
var li = null;
for (var i=0; i<alphabet.length; i++) {
li = document.createElement('li');
li.innerHTML = alphabet[i];
ul.appendChild(li);
m_Buttons.push (li);
}
myButtons.appendChild(ul);
}
function getButton(button_index) {
m_Buttons[button_index].style.color = 'red';
return Buttons[button_index];
}
</script>
</head>
<body onload='Init();'>
<div id='buttons'>
</div>
<button onclick='getButton(3);'> Click to get button 3 </button>
</body>
</html>
Related
Pardon the bad title, it's hard to explain. If you know how to phrase it better, please comment and I will update as soon as I can.
So, I was messing around with a random generator site (perchance.org) and writing my own HTML/Javascript to make my generator work. It has a behavior that is what I want, but that shouldn't be happening according to my knowledge of HTML.
Let me explain with a minimal example.
The example code here is to produce a simple page that has a button.
This button should generate <input>s with <button>s next to them, attached with similar ID's.
The button, when clicked, deletes the <input> and <button>.
Here is a snippet to show you the code/let you reproduce the results:
<html>
<head>
<script>
var current_id = 0;
function add_input () {
var list = document.getElementById("list");
var input = document.createElement("input");
var delete_button = document.createElement("button");
var br = document.createElement("br");
input.id = "input_" + current_id;
delete_button.id = "button_" + current_id;
br.id = "br_" + current_id;
input.value = input.id;
delete_button.textContent = "Delete";
delete_button.onclick = function () {
delete_input(this.id.slice(7)) //To get the numerical ID
}
list.appendChild(input);
list.appendChild(delete_button);
list.appendChild(br);
current_id++;
}
function delete_input (id) {
var input = document.getElementById("input_"+id);
var button = document.getElementById("button_"+id);
var br = document.getElementById("br_"+id);
input.remove();
button.remove();
br.remove();
current_id--;
}
</script>
</head>
<body>
<div id="list">
</div>
<button onclick="add_input()">Add</button>
</body>
</html>
When you add two inputs, then delete the first, and add one more, it leaves you with two inputs using the same ID. It also leaves you with two buttons with the same ID. And yet, both buttons delete their intended target.
Why?
You really should delegate - here I wrap in a div that can be removed in one go
You can rename each input to have incremented IDs but just letting the cnt run, gives you unique IDs
let cnt = 0;
function add_input() {
var list = document.getElementById("list");
var div = document.createElement("div");
var input = document.createElement("input");
var delete_button = document.createElement("button");
var br = document.createElement("br");
input.id = "input_" + (cnt++)// list.querySelectorAll("div").length
input.value = input.id;
delete_button.textContent = "Delete";
delete_button.classList.add("delete")
div.appendChild(input);
div.appendChild(delete_button);
div.appendChild(br);
list.appendChild(div);
}
window.addEventListener("load", function() {
document.getElementById("list").addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("delete")) tgt.closest("div").remove();
})
})
<div id="list">
</div>
<button onclick="add_input()">Add</button>
I changed your code to be more effective.
I'm not using IDs as they aren't adding any benefit instead making it more complex.
Instead I target the element via the event handler and an argument.
I also wrap each set of inputs/buttons in a div so I can just remove that div and it will remove all of the children.
function add_input() {
var list = document.getElementById("list");
var input = document.createElement("input");
var delete_button = document.createElement("button");
var br = document.createElement("br");
delete_button.textContent = "Delete";
delete_button.onclick = function(e) {
e.target.parentNode.remove();
}
var div = document.createElement("div");
div.appendChild(input);
div.appendChild(delete_button);
div.appendChild(br);
list.appendChild(div)
}
<div id="list">
</div>
<button onclick="add_input()">Add</button>
I am building a simple JavaScript to-do list with DOM methods and am attempting to implement localStorage for the list items. localStorage appears to function properly when items are both added and removed. However, the word undefined is thrown to the screen before the list items are rendered. Any idea why this is happening? Much appreciated!
JS:
var ul = document.getElementById("myUl");
function add() {
var item = document.getElementById("newItem").value;
var itemTxt = document.createTextNode(item);
var li = document.createElement("li");
var btn = document.createElement("button");
var btnx = document.createTextNode("x");
btn.setAttribute("onclick", "remove()");
btn.appendChild(btnx);
li.appendChild(itemTxt);
li.appendChild(btn);
ul.appendChild(li);
}
function remove() {
var task = this.event.currentTarget.parentNode;
ul.removeChild(task);
}
ul.innerHTML = localStorage["list"];
setInterval(function(){
localStorage["list"] = ul.innerHTML;
}, 1000);
HTML:
<input id="newItem" />
<button onclick="add()">add</button>
<ul id="myUl">New List</ul>
I updated code with comments:
var ul = document.getElementById("myUl");
function add() {
var item = document.getElementById("newItem").value;
var itemTxt = document.createTextNode(item);
var li = document.createElement("li");
var btn = document.createElement("button");
var btnx = document.createTextNode("x");
btn.setAttribute("onclick", "remove()");
btn.appendChild(btnx);
li.appendChild(itemTxt);
li.appendChild(btn);
ul.appendChild(li);
localStorage["list"] = ul.innerHTML // updating localstorage
}
function remove() {
var task = this.event.currentTarget.parentNode;
ul.removeChild(task);
localStorage["list"] = ul.innerHTML // updating localstorage
}
if (localStorage["list"]) { // checking, if there is something in localstorage
ul.innerHTML = localStorage["list"];
}
Demo: https://codepen.io/anon/pen/dQpwpz
NB: It is not a best practice to put html into localStorage, but I believe you know it.
I want to create a to-do list with an array, functions and js must be separated from HTML. The HTML part is fine, but I have troubles finishing the functions.
Also the EventListener works.
<form id="todoForm">
<input id="todoInput" type="text">
<button type="button" id="button">Add your To Do</button>
</form>
<ol id="toDoList"></ol>
We have the array 'todos', the button click is noticed, and the function 'addTodo' is called, which (I hope so) pushes the input into the array.
I don't know how to call next function, which should create the array items as li elements, that is the place where I need help. The content in function 'printTodos' is garbage.
var todos = [];
document.getElementById('button').addEventListener('click', function
addTodo () {
todos.push('input')
function printTodos () {
var item = document.createElement("li");
var node = createTextNode(input);
// I am stuck
}
});
You are very close, except you do not want to create the addTodo and printTodos function inside your click event listener.
What you can instead do is define the two functions outside of it, and call them inside your click event listener, like so:
var todos = [];
function addTodo() {
var inputValue = document.getElementById('todoInput').value;
todos.push(inputValue);
}
function printTodos() {
var list = document.getElementById('toDoList');
list.innerHTML = ''; //Reset the list content whenever we print the todos, so we don't end up with duplicate todo items.
for (var i = 0; i < todos.length; i++) {
var li = document.createElement('li');
var listItem = li.appendChild(document.createTextNode(todos[i]));
list.appendChild(listItem);
}
}
document.getElementById('click', function() {
addTodo();
printTodos();
});
What we do here is, in the addTodo function, we programmatically get the text typed into the todoInput and add it to the array. Then, in the printTodos function, we loop over all the entered todos and create <li> element filled with the todo text. At the end, we append the new list item to the toDosList.
I've made a fully working code pen example for you. Please take a look on it or go with the above answer. Here is what you need https://codepen.io/waleedbinkhalid/pen/aRvwmo
var todos = [];
document.getElementById('button').addEventListener('click', function () {
var list = $('#toDoList');
var todoInput = $('#todoInput').val();
todos.push(todoInput)
var item = document.createElement("li");
for (var i = 0; i < todos.length; i++) {
var listItem = $(document.createTextNode(todos[i]));
list.append(document.createTextNode(todos[i]));
}
});
`
I am building a todo app where I am dynamically generating tasks using javascript.[]
I generate following equivalent html from js whenever I click on the add button:
<div class="row datasection">
<div class="todo">
<div class="databox col s6 offset-s1 waves-effect">
<p class="checkglyph1 checkglyph2">Task no 1</p>
<a>
<i class="material-icons checkglyph checkglyph1 checkglyph2 glyphcolor">check</i>
</a>
</div>
</div>
</div>
Now what I want is whenever I click event on the task created it should become yellow in colour.I have written the code to this. below.However it works fine only when there is one task created.If there is multiple then the last task works well but actionlistener on the first one does not seem to be working.I am not able to figure out where the code is breaking.
var glyph= document.querySelectorAll(".glyphcolor");
for (var i = 0; i < glyph.length; i++) {
glyph[i].addEventListener('click', function(event) {
this.classList.toggle("checkglyph1");
});
}
The actual snippet
//declaration
var calendardata = document.getElementById('date1');
var addbutton = document.querySelector('.addbutton');
var todo = document.querySelector('.todo');
addbutton.addEventListener('click', function() {
/* body to return the html */
if (data.value) {
var newdiv = document.createElement("div"); // Create a <button> element
newdiv.classList.add("databox", "col", "s6", "waves-effect");
//console.log(newdiv);
todo.appendChild(newdiv);
//console.log(newdiv.parentNode);
var newpar = document.createElement("p");
newpar.classList.add("checkglyph1", "checkglyph2");
var node = document.createTextNode(data.value + "." + " " +
calendardata.value);
var newa = document.createElement("a");
newdiv.appendChild(newa)
var newglyph = document.createElement("i");
newglyph.classList.add("material-icons", "checkglyph", "checkglyph1",
"checkglyph2", "glyphcolor");
var node1 = document.createTextNode("check");
newa.appendChild(newglyph);
newglyph.append(node1);
newpar.appendChild(node);
newdiv.appendChild(newpar);
data.value = "";
calendardata.value = "";
created = true;
// console.log("before glyh created");
//code to perform action on the click of the tick symbol
var glyph = document.querySelectorAll(".glyphcolor");
var par = document.getElementsByClassName('checkglyph2');
for (var i = 0; i < glyph.length; i++) {
//console.log("Inside the loop");
glyph[i].addEventListener('click', function.bind(event) {
this.classList.toggle("checkglyph1");
//console.log('Inside the click');
//console.log(i);
});
}
}
})
What's happening that when they other tasks are created they aren't being collected in the Node Collection, thus they have no event listener. What you can do is instead add the event listener to the container and change whichever item that was clicked in:
document.querySelector('ul').addEventListener('click', changeClass);
document.querySelector('#button').addEventListener('click', addLi);
function changeClass(e){
e.target.closest('li').classList.toggle('checkglyph1');
}
function addLi(e){
const new_li = document.createElement('li');
new_li.textContent = document.querySelectorAll('li').length + 1;
new_li.classList.add('checkglyph1');
document.querySelector('ul').appendChild(new_li);
}
li:not(.checkglyph1) {
background: #f00;
}
<button id="button">Add li</button>
<ul>
<li class="checkglyph1">1</li>
<li class="checkglyph1">2</li>
<li class="checkglyph1 red">3</li>
<li class="checkglyph1">4</li>
<li class="checkglyph1">5</li>
<li class="checkglyph1">6</li>
<li class="checkglyph1">7</li>
<li class="checkglyph1">8</li>
<li class="checkglyph1">9</li>
<li class="checkglyph1">10</li>
</ul>
The problem with your code is that everytime you click on ".addbutton", you're going through all of the ".glyphcolor"s element's in your DOM and adding a new onclick event listener. The last one created will work, but the others will have multiple event listeners repeated (yes, this is possible). So, when you click on an element that has two events telling it to toggle the "checkglyph1" class, it will do it twice. And of course, it will not change at all. You can easily see this happening, because all the odd elements in your page must work (they will toggle the class by odd times), while the even ones must not.
This can be corrected by adding the event listener directly on the element when you create it on the page. The code below must work fine:
//declaration
var calendardata = document.getElementById('date1');
var addbutton = document.querySelector('.addbutton');
var todo = document.querySelector('.todo');
addbutton.addEventListener('click', function() {
if (data.value) {
var newdiv = document.createElement("div"); // Create a <button> element
newdiv.classList.add("databox", "col", "s6", "waves-effect");
//console.log(newdiv);
todo.appendChild(newdiv);
//console.log(newdiv.parentNode);
var newpar = document.createElement("p");
newpar.classList.add("checkglyph1", "checkglyph2");
var node = document.createTextNode(data.value + "." + " " +
calendardata.value);
var newa = document.createElement("a");
newdiv.appendChild(newa)
var newglyph = document.createElement("i");
newglyph.classList.add("material-icons", "checkglyph", "checkglyph1",
"checkglyph2", "glyphcolor");
var node1 = document.createTextNode("check");
newa.appendChild(newglyph);
newglyph.append(node1);
newpar.appendChild(node);
newdiv.appendChild(newpar);
data.value = "";
calendardata.value = "";
created = true;
newglyph.addEventListener('click', function(event) {
event.preventDefault(); // Just to prevent non desired effects of click
newglyph.classList.toggle('checkglyph1');
}
}
}
And just small clarifications: you don't need the "bind" in your actual code.
glyph[i].addEventListener('click', function.bind(event) { // just type function(event)
this.classList.toggle('checkglyph1');...
I have tried to create a function that creates a dynamic menu. Ive been able to create the "a" tags and give them individual links while also assigning them ID's. The problem is that I cant get the links inside of a list where my CSS applies its rules.
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"]
var ul = document.createElement("ul");
document.getElementById("mainNav").appendChild(ul);
for (i = 0 ; i < links.length ; i++) {
var ul = document.createElement("ul");
var a = document.createElement("a");
var text = document.createTextNode(links[i]);
a.href = links[i];
a.id = "mainNav";
a.text = links[i];
document.getElementById("mainNav").appendChild(a);
}
}
Any suggestions on cleaning the code while keeping to javascript would be appreciated. And also any explanation of syntax would be also appreciated.
Thank you!
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"];
var ul = document.createElement("ul");
var li, a, text;
for (var i = 0, l = links.length; i < l; ++i) {
li = document.createElement('li');
a = document.createElement('a');
text = document.createTextNode(links[i]);
a.href = links[i];
a.setAttribute("class", "some-class-name");
a.appendChild(text);
li.appendChild(a);
ul.appendChild(li);
}
document.querySelector("#mainNav").appendChild(ul);
}
• Use querySelector over getElementById. Both work, but theres a performance boost to querySelector. The API is close to jQuery and most (if not all) newer browsers support querySelector.
• Append the ul AFTER you've added the elements again for performance reasons.
• Use an LI to hold the link element, a second UL wont do what you want.
• Don't resuse id's, the thing you would want to use is a class, they basically do the same thing but javascript treats id's and classes differently. If that doesnt fit your needs, try a compound CSS selector in your css such as:
#mainNav ul li a { /* styles here */ }
You have to ensure that you append the correct items to the correct parents. Scanning your code I assume you want the following HTML output:
<div id="mainNav"> // this is already there
<ul>
<li>
intro.html
</li>
... // repeat for other items in the array
</ul>
</div>
You can modify your code like this to get the above result:
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"]
var ul = document.createElement("ul");
document.getElementById("mainNav").appendChild(ul);
for (i = 0 ; i < links.length ; i++) {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = links[i];
a.className = "some-class-to-target-in-your-css";
a.innerText = links[i];
ul.appendChild(li);
li.appendChild(a);
}
}
Your approach isn't that bad. I at one time took a similar outlook towards doing these. However, I am now of the belief it is far more testable, reliable, and easier to build these as actual templates, clone them, and populate them through a factory pattern.
In your setup, you have a parent ul, and then build multiple ul's with a's in them -- I am going to assume you meant to nest li elements -- and so that is what this will do.
Here is how I would approach that in your scenario.
Step 1: Build a the template, and create the styling and visual effect.
.navLink a{
padding:3px;
}
<ul class="menuTemplate">
<li class="navLink"><a></a></li>
</ul>
Simple style, I know. But it is just for simplicity, you can really do whatever you want there to style the example. Note that it is a simple structure, so all you are really seeing in there is a template, an li element, and the a element.
What we are also going to add to the style definition in our use case is
.menuTemplate{
display:none;
}
Because we don't actually want to see the template, we just want to use it. Next, we will create a factory for these.
var navFactory = new function(){
var template = document.querySelector('.menuTemplate .navLink');
this.Create = function(text,href){
var nav = template.cloneNode(true);
var link = nav.querySelector('a');
link.href = href;
link.innerText = text;
return nav;
}
};
The last step is to simply take your element that will hold the nav elements - you named this "mainNav" above - and fill it in from the factory.
var navFactory = new function(){
var template = document.querySelector('.menuTemplate .navLink');
this.Create = function(text,href){
var nav = template.cloneNode(true);
var link = nav.querySelector('a');
link.href = href;
link.innerText = text;
return nav;
}
};
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"];
var navUl = document.querySelector('#mainNav ul');
for(var i = 0; i < links.length; i++){
navUl.appendChild(navFactory.Create(links[i],links[i]));
}
}
write_navBar();
.menuTemplate{
display:none;
}
.navLink a{
padding:3px;
opacity:0.85
}
.navLink a:hover{
opacity:1;
}
<ul class="menuTemplate">
<li class="navLink"><a></a></li>
</ul>
<div id="mainNav">
<ul></ul>
</div>