Suppose I have 3 items and I would like to add them one by one in array.
Firstly, I pass abc and it adds abc in array, second time, passed cde, then array removes the first item and add second item cde.
But, I would like to add them like array={abc,cde,...}
It seems that I need to store previous value. First time, when I add abc it looks like array ={abc}. Second time, when I add cde, array should store the previous abc value before adding the new one cde which looks like array ={abc,cde,..}.
Please see this screenshot:
There is no array of the items where I can loop through and add those in another array.
This line always var array= Arr.push(Arr[0]); add one item.
var Arr ="";
var TD;
function AddData(sVal) {
var AddRow = true;
sVal = Replace(sVal, "~", "%");
Arr = sVal.split("^");
TD.innerHTML = "<INPUT TYPE='HIDDEN' value='" + Arr[0] + "'>";
TD.innerHTML = Arr[0];
var array= Arr.push(Arr[0]);
}
}
New approach
I separated the previous approach from the new one...
Having a source list that feeds the destination list when its items get clicked:
Here I show how you can have a source list with items feeding a destination list:
When you click on a source list item, it gets appended to the destination as is (cloned)
At the same time, during the adding operation, the item text content gets appended to a buffer array
There's also a function that dumps on console an array that lists all the items coming from a given list element (at page load)
Clicking on an item on destination, it gets removed from the list and from the buffer array
Items cannot be duplicated on destination so that if you click the first time on a list item in source, it gets appended on destination. The second time it won't have any effect. But after that said item gets removed from destination, it can be added again
const source = document.getElementById('source');
const destination = document.getElementById('destination');
//print on console the list of items in #source as an array of string values
console.log('source list items:', getListItems(source));
//adds the click event listener to the #source list
source
.addEventListener('click',(event)=>{
//clicked item
const target = event.target;
//if the clicked item is actually a <li>
if(target.tagName == 'LI' && !target.classList.contains('alreadyadded')){
//calls addItemToList on destination with the current target list item
addItemToList(destination, target);
target.classList.add('alreadyadded');
}
});
//adds the click event listener to the #destination list
destination
.addEventListener('click',(event)=>{
//clicked item
const target = event.target;
//if the clicked item is actually a <li>
if(target.tagName == 'LI'){
//removes the class alreadyadded from the corresponding item in the source
const sourceIndex = target.dataset.sourceIndex;
source.querySelectorAll('li')[sourceIndex].classList.remove('alreadyadded');
//calls addItemToList on destination with the current target list item
removeItemFromList(target);
}
});
//the array that gets populated by the function addItemToList and cleared by removeItemFromList
const destinationList = [];
//adds a list item to the target list element
function addItemToList(target, item){
//gets the index of the item compared to its parent list
const index = [...item.parentNode.children].indexOf(item);
//clones deep the item passed
const newItem = item.cloneNode(true);
//sets its data attribute (sourceIndex) with the index of the item in the source list
newItem.dataset.sourceIndex = index;
//appends it to target element
target.append(newItem);
//updates the buffer array pushing the content of the new item
destinationList.push(newItem.textContent);
//logs on console the buffer array
console.log('buffer array:', destinationList);
}
//removes a list item from its parent
function removeItemFromList(item){
//gets the index of the item compared to its parent list
const index = [...item.parentNode.children].indexOf(item);
//removes the element from dom
item.remove();
//removes the index element from the destinationList array
destinationList.splice(index, 1);
//logs on console the buffer array
console.log('buffer array:', destinationList);
}
//returns the array of item contents from the target list element
function getListItems(target){
const listItems = target.querySelectorAll('li');
return [...listItems].map(li => li.innerText);
}
ul{
list-style: none;
padding: 0;
width: fit-content;
border-top: solid 1px gray;
}
li{
border: solid 1px gray;
border-top: none;
padding: 0 1em;
}
#source li{
cursor: pointer;
}
#destination li::before{
content: 'x';
color: red;
cursor: pointer;
border: solid 1px blue;
padding: 0 2px 2px 2px;
margin-right: 7px;
display: inline-block;
line-height: 1ch;
border-radius: 3px;
margin-bottom: 4px;
}
<label>Source:</label>
<ul id="source">
<li>test1</li>
<li>test2</li>
<li>test3</li>
<li>test4</li>
</ul>
<label>Destination:</label>
<ul id="destination">
</ul>
Previous approach
Here follows the previous approach...
Pushing an array of values into a target array:
At first it seemed like you needed to push a list of values inside an array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
The push() method adds one or more elements to the end of an array and
returns the new length of the array.
push(element0)
push(element0, element1)
push(element0, element1, /* … ,*/ elementN)
const target = [1, 2, 3];
const elementsToAdd = [4, 5, 6];
target.push(...elementsToAdd);
console.log(target); //[ 1, 2, 3, 4, 5, 6 ]
Adding a new row to a target table using values from a serialized string:
Trying to further understand your code, I tried to rewrite your addData function to limit its action doing what its name suggests (adding data).
Well actually such name could suggest several different actions anyway to shed some light on what exactly did you expect I stripped down its core so that now it just limits itself to append a new row to the target table using the values coming from a serialized string for its cells.
Those cells will contain an <input type=hidden> with the value of the corresponding index from the serialized string. To better show off the otherwise invisible data, I decided also to set the cell innerText with that same value.
Of course that's not the whole story yet
//appends 10 rows to the target table
const table = document.getElementById('Table1');
for(let i=0;i<10;i++){
addData(table, 'col~1^col~2^col~3^col~4');
}
//appends a row in target table, with the content coming from raw
function addData(target, raw) {
//replaces the ~ with % on raw,
const processed = raw.replaceAll('~', '%');
console.log(processed);
//and splits its values delimited by ^
const values = processed.split("^");
//creates and append a new row in target table
const newRow = target.insertRow(-1);
//for each value in values
for(value of values){
//creates a new <input type=hidden>
const input = document.createElement('input');
input.setAttribute('type', 'hidden');
//sets its value as the current value of the array
input.value = value;
//creates a new row cell appending the input to its children
newCell = newRow.insertCell(-1);
newCell.append(input);
//since the only cell content is an hidden input I show it on its content also
newCell.textContent = value;
}
}
body{
padding: 1rem;
}
table{
border-collapse: collapse;
width: 100%;
}
table tr{
height: 2rem;
}
table td{
border: solid 1px;
}
<table id="Table1">
<tr id="TR01">
<td>header #1</td>
<td>header #2</td>
<td>header #3</td>
<td>header #4</td>
</tr>
</table>
Related
I'm trying to create a to-do list application. I'm using JS to dynamically create list elements on the web page when a user clicks on the submit button along with their user input.
Here's what I have so far:
const inputTXT = document.querySelector('.inputText'); //input element
const submitBUTT = document.querySelector('.submitButton'); //button element
const listITEMS = document.querySelector('.items'); //list element
function createListElement(inputString){
const newListItem = document.createElement("li");
const newEditButton = document.createElement("button");
const newDeleteButton = document.createElement("button");
const listText = document.createTextNode(inputString);
newListItem.appendChild(listText);
const editText = document.createTextNode("Edit");
newEditButton.appendChild(editText);
const deleteText = document.createTextNode("Delete");
newDeleteButton.appendChild(deleteText);
newListItem.appendChild(newEditButton);
newListItem.appendChild(newDeleteButton);
//assign class to each list element for line below:
newDeleteButton.className = "deleteCLASS";
newEditButton.className = "editCLASS";
//delete function:
var deleteButtonArray = document.querySelectorAll(".deleteCLASS");
for(var i=0; i < deleteButtonArray.length ; ++i){
deleteButtonArray[i].onclick = function(){
this.parentNode.remove();
}
}
return newListItem;
}
function addTask(){
listITEMS.classList.remove('hidden');
const ITEM = createListElement(inputTXT.value);
document.getElementsByClassName("items")[0].appendChild(ITEM);
inputTXT.value = ''; //Resets user input string
}
submitBUTT.addEventListener("click", addTask);
*{
margin: 0;
padding: 0;
}
.container{
width: 800px;
margin: 0 auto;
font-family: sans-serif;
}
.main-header{
background: #e7e7e7;
text-align: center;
margin: 15px;
padding: 20px;
}
.inputText{
margin-top: 20px;
}
h1{
font-size: 35px;
}
ul{
list-style: square;
margin-left: 275px;
}
.hidden{
display: none;
}
.itemLIST{
margin-bottom: 5px;
}
.editBUTT{
margin-left: 20px;
}
.deleteBUTT{
margin-left: 4px;
}
<!DOCTYPE html>
<head>
<title> "To-do List" </title>
<link href="style.css" rel="stylesheet"/>
<meta charset = "UTF-8"/>
</head>
<body>
<div class = "container">
<div class = "main-header">
<h1>My To-do List</h1>
<input type="text" placeholder = "Enter Item" class= "inputText" required>
<button class = "submitButton">Submit</button>
</div>
<ul class ="items hidden">
</ul>
</div>
<script src="script.js"></script>
</body>
</html>
This works for the most parts, but I cannot delete the last list element for some reason.
The only debugging I've done to figure out what is happening is print out the "deleteButtonArray" variable, which uses the keyword "querySelectorAll". From this, I found out that when the user hits the submit button, an empty NodeList printed. When the user hits the submit button a second time, only then do we get a NodeList with 1 element. When the user hits the submit button a third time, we get a NodeList with 2 elements.
It looked like the problem had something to do with querySelectorAll not properly updating in time when the user hits the submit button. I replaced it with getElementsByClassName, and still the same issue.
Now, I think the problem has something to do with the way I'm trying to implement the delete function within the createListElement function.
The way I think my code works:
Every time a user hits the submit button, a list element is created along with an array (actually a NodeList) that contains all the list elements present so far. This means that if I delete list elements, the array will update with the correct number of list elements.
And, it does update correctly, for the most parts. I just don't know why the array is empty when we first create an element. Shouldn't querySelectorAll or getElementsByClassName return a non-empty NodeList when the user first hits the submit button?
All right, so here's a solution I've tried and tested it, and it seems to be working.
I removed the delete item portion out of the createListElement function, made it its own function, and added an event listener to each delete button that's created, so it will run the now separate delete function when clicked.
No changes were made to the HTML or the CSS.
const inputTXT = document.querySelector('.inputText'); //input element
const submitBUTT = document.querySelector('.submitButton'); //button element
const listITEMS = document.querySelector('.items'); //list element
function createListElement(inputString){
const newListItem = document.createElement("li");
const newEditButton = document.createElement("button");
const newDeleteButton = document.createElement("button");
const listText = document.createTextNode(inputString);
newListItem.appendChild(listText);
const editText = document.createTextNode("Edit");
newEditButton.appendChild(editText);
const deleteText = document.createTextNode("Delete");
newDeleteButton.appendChild(deleteText);
newListItem.appendChild(newEditButton);
newListItem.appendChild(newDeleteButton);
//Here is the new addEventListener
newDeleteButton.addEventListener("click", deleteStuff);
//assign class to each list element for line below:
newDeleteButton.className = "deleteCLASS";
newEditButton.className = "editCLASS";
return newListItem;
}
//Here is the new delete function. The onclick function that was there before has been removed because, in this case, it's not needed.
function deleteStuff() {
//delete function:
var deleteButtonArray = document.querySelectorAll(".deleteCLASS");
for(var i=0; i < deleteButtonArray.length ; ++i){
this.parentNode.remove();
}
}
function addTask(){
listITEMS.classList.remove('hidden');
const ITEM = createListElement(inputTXT.value);
document.getElementsByClassName("items")[0].appendChild(ITEM);
inputTXT.value = ''; //Resets user input string
}
submitBUTT.addEventListener("click", addTask);
I'm trying to loop through array of objects to display them on a grid based off of input values for my library project. My code for loop is:
const addBook = (ev) => {
ev.preventDefault();
let myLibrary = [];
let bookInfo = {
title: document.getElementById('title').value,
author: document.getElementById('author').value,
pages: document.getElementById('pages').value,
}
myLibrary.push(bookInfo)
for (i = 0; i < myLibrary.length; i++) {
console.log(myLibrary)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
var submitBtn = document.querySelector('.submit-btn');
submitBtn.addEventListener('click', addBook)
Each time I enter title,author, and pages value and click submit button, it goes through and gives me a cell. If try to add another book, it gives me 3 cells rather then 2. Doing another add gives me 6 instead of 3. How can I make it where I can add a book each time one by one rather then it adding multiple times?
There are three main issues.
A missing curly brace after the function and before the button listener.
myLibrary is being redefined each time the function is called which is one of the reasons you're having to look over the data each time. You want to define it outside the function so you can add one book at a time to it when addBook is called.
With myLibrary no longer being redefined each time there's no need for the loop. We can just add the HTML for the book to the book shelf onSubmit.
(Note: In this working example (I've added some HTML for a table, and the inputs and buttons, and created some code to add the new book to the shelf), I've renamed the myLibrary variable to bookShelf to keep things consistent with the HTML naming.)
// Cache all the elements up front
const titleEl = document.getElementById('title');
const authorEl = document.getElementById('author');
const pagesEl = document.getElementById('pages');
const bookshelfEl = document.getElementById('bookshelf');
const submitBtn = document.querySelector('.submit-btn');
// Add the listener
submitBtn.addEventListener('click', addBook, false);
titleEl.focus();
// Our bookShelf variable is now
// outside the function
const bookShelf = [];
function addBook() {
// Because we've cached the elements
// we can now just grab the values from each
const bookInfo = {
title: titleEl.value,
author: authorEl.value,
pages: pagesEl.value,
}
bookShelf.push(bookInfo);
// Once we've added our book we can grab the
// title, author, and pages variables from it
const { title, author, pages } = bookInfo;
// Create a row for the table
const row = document.createElement('tr')
// Create some HTML and add it to the div
row.innerHTML = `
<td>${title}</td>
<td>${author}</td>
<td>${pages}</td>
`;
bookshelfEl.appendChild(row);
}
table { border-collapse: collapse; border: 2px solid #565656; width: 100%; }
td { text-align: center; }
.heading { background-color: #efefef; border-top: solid thin #565656; }
tr { border: solid thin #ababab; }
<input placeholder="Title" id="title" />
<input placeholder="Author" id="author" />
<input placeholder="Pages" id="pages" />
<button type="button" class="submit-btn">Submit</button>
<H3>Book shelf</H3>
<table>
<tbody id="bookshelf">
<tr class="heading">
<td>Title</td>
<td>Author</td>
<td>Pages</td>
</tr>
</tbody>
</table>
Additional documentation
Destructuring assignment
Template/string literals
Your issue is with the loop. Each time you click the submit button, you add a cell for each item in myLibrary in this block:
for (i = 0; i < myLibrary.length; i++) {
console.log(myLibrary)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
I don't think you need to loop through the array each time. Just add the cell directly whenever you are adding bookInfo data to myLibrary, without using for loop e.g
const addBook = (ev) => {
ev.preventDefault();
let myLibrary = [];
let bookInfo = {
title: document.getElementById('title').value,
author: document.getElementById('author').value,
pages: document.getElementById('pages').value,
}
myLibrary.push(bookInfo)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
var submitBtn = document.querySelector('.submit-btn');
submitBtn.addEventListener('click', addBook)
In this program, I'm able to add inputs with a button but I need to show the length of each input as it changes. I'm able to get the length using an EventListener, but I'm not sure how to change the text value for any newly created buttons.
On line 12, you can see that I'm able to change the value successfully on the first input but I'm using an html variable. If you look at my addCell() function, you'll see that I have an element as a child of each node to keep track of the length of each input. I need to access that element in my change() function so I can set the event.target.value.length to the corresponding nodes child element.
I've tried using this, setting var x = this and I've tried using the event.target properties to find the corresponding node and even innerHTML.
var i = 0;
var count = 1;
var length = 2;
var chars = 0;
document.addEventListener('input', function (evt) {
change(evt);
});
function change(elem) {
var check = document.getElementById("first");
if (event.target == check) {
document.getElementById("len").innerHTML = event.target.value.length;
return;
}
// Here's where I'm stuck
}
function removeCell() {
if (count <= 1) {
alert("Illegal operation, the police have been notified.")
return;
}
var elem = document.getElementById('main');
elem.removeChild(elem.lastChild);
count = count - 1;
length = length - 1;
}
function addCell() {
var node = document.createElement('div');
node.innerHTML += length;
var inp = document.createElement('INPUT');
var size = document.createElement('size');
inp.setAttribute("type", "text");
node.appendChild(inp);
node.appendChild(size);
document.getElementById('main').appendChild(node);
count += 1;
length += 1;
i += 1;
}
#area {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
#texts {
width: 220px;
height: 50px;
border-style: solid;
}
body {
background-color: grey;
}
<div id="area">
<form id="main">
<pre><b> input </b> length</pre>
<span id="list">
1<input type="text" id="first"> <var id="len"></var>
</span>
</form>
<br />
<button onclick="addCell()">Add Cell</button>
<button onclick="removeCell()">Remove Cell</button>
<button onclick="sort()">Sort</button>
</div>
Since I'm able to use alert() to show me the correct length of each newly created input each time it changes, I know there's a way to access the "size" element I created to update it using event.target.value.length
Your problem is that you use a "global" input event listener and your change() function is not programmed to handle multiple input fields because in it you are querying known element ids first and len.
If you want to go with a global listener you have to tell your change() function how to access the new input and corresponding target fields.
An easier way is that you modify your addCell() function and attach an event listener to the input field that you are creating instead of using a global one. Thereby each input field holds its own event listener. Since both the input field and your size element, which displays the length of the input value, are created in the same scope you can use easily write the length to the corresponding size element.
inp.addEventListener('input', function(){
size.innerText = inp.value.length;
});
If you want this to work with your provided HTML you need to remove your first input field and call addCell() manually so that your initial input gets rendered.
Your code should then look like this (note: I set var count = 0; and var length = 1;):
var i = 0;
var count = 0;
var length = 1;
var chars = 0;
function removeCell() {
if (count <= 1) {
alert("Illegal operation, the police have been notified.")
return;
}
var elem = document.getElementById('main');
elem.removeChild(elem.lastChild);
count = count - 1;
length = length - 1;
}
function addCell() {
var node = document.createElement('div');
node.innerHTML += length;
var inp = document.createElement('INPUT');
var size = document.createElement('size');
inp.setAttribute("type", "text");
inp.addEventListener('input', function(){
size.innerText = inp.value.length;
});
node.appendChild(inp);
node.appendChild(size);
document.getElementById('main').appendChild(node);
count += 1;
length += 1;
i += 1;
}
addCell();
#area {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
#texts {
width: 220px;
height: 50px;
border-style: solid;
}
body {
background-color: grey;
}
<div id="area">
<form id="main">
<pre><b> input </b> length</pre>
<span id="list"></span>
</form>
<br />
<button onclick="addCell()">Add Cell</button>
<button onclick="removeCell()">Remove Cell</button>
<button onclick="sort()">Sort</button>
</div>
If HTML layout is planned out and is consistent you can use [name] attribute for form controls and .class or even just the tagName. Use of #id when dealing with multiple tags is difficult and unnecessary. Just in case if you weren't aware of this critical rule: #ids must be unique there cannot be any duplicate #ids on the same page. Having duplicate #ids will break JavaScript/jQuery 90% of the time.
To accessing tags by .class, #id, [name], tagName, etc. use document.querySelector() and document.querySelectorAll() for multiple tags.
To access forms and form controls (input, output, select, etc) by [name] or #id use the HTMLFormElement and HTMLFormControlsCollection APIs.
.innerHTML is destructive as it overwrites everything within a tag. .insertAdjacentHTML() is non-destructive and can place an htmlString in 4 different positions in or around a tag.
Event handlers and event listeners work only on tags that were initially on the page as it was loaded. Any tags dynamically added afterwards cannot be registered to listen/handle events. You must delegate events by registering an ancestor tag that's been on the page since it was loaded. This was done with delRow() since the buttons are dynamically created on each row (changed it because one delete button that removes the last row isn't that useful. ex. 7 rows and you need to delete 4 rows just to get to the third row).
Here's a breakdown of: [...ui.len] ui references all form controls .len is all tags with the [name=len]. The brackets and spread operator converts the collection of len tags to an array.
There's no such thing as <size></size>. So document.createElement('size') is very wrong.
const main = document.forms.main;
main.oninput = count;
main.elements.add.onclick = addRow;
document.querySelector('tbody').onclick = delRow;
function count(e) {
const active = e.target;
const ui = e.currentTarget.elements;
const row = active.closest('tr');
const idx = [...row.parentElement.children].indexOf(row);
const length = [...ui.len][idx];
length.value = active.value.length;
return false;
}
function addRow(e) {
const tbody = document.querySelector('tbody');
let last = tbody.childElementCount+1;
tbody.insertAdjacentHTML('beforeend', `<tr><td data-idx='${last}'><input name='txt' type="text"></td><td><output name='len'>0</output></td><td><button class='del' type='button'>Delete</button></td>`);
return false;
}
function delRow(e) {
if (e.target.matches('.del')) {
const row = e.target.closest('tr');
let rows = [...row.parentElement.children];
let qty = rows.length;
let idx = rows.indexOf(row);
for (let i = idx; i < qty; i++) {
rows[i].querySelector('td').dataset.idx = i;
}
row.remove();
}
return false;
}
body {
background-color: grey;
}
#main {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
tbody tr td:first-of-type::before {
content: attr(data-idx)' ';
}
<form id="main">
<table>
<thead>
<tr>
<th class='txt'>input</th>
<th class='len'>length</th>
<th><button id='add' type='button'>Add</button></th>
</tr>
</thead>
<tbody>
<tr>
<td data-idx='1'><input name='txt' type="text"></td>
<td><output name='len'>0</output></td>
<td><button class='del' type='button'>Delete</button></td>
</tr>
</tbody>
</table>
<!--These are dummy nodes because of the
HTMLFormControlsCollection API ability to use id or name, there
must be at least 2 tags with the same name in order for it to
be considered iterable-->
<input name='txt' type='hidden'>
<input name='len' type='hidden'>
</form>
My header on my website has 5 tabs (they are div tags all named "dropdown". Under each tab are clickable links (the anchor tags). I am trying to write some code that will print text in the console when a link is click and tell the user about where it was clicked (it uses the innerText). For instance, if a user clicks a link under the first tab, it will log "column 1| Link1" or if a user clicks on a link in the second tab "column 2| link 3". All I have is the nested for loop that will loop through the anchor tags under each div tag but I am not sure if its correct. This is what I have:
var dropdownDivs = document.getElementsByClassName('dropdown');
for(i = 0; i < dropdownDivs.length;i++) {
var lnks =
document.getElementsByClassName('dropdown').getElementsByTagName('a');
for(i = 0; i < dropdownDivs.length;i++){
for (var l in lnks) {
}};
In order to get the placement index of the DIV and the Link(anchor tag) on the page you're going to need to collect at least one of them into an array to grab their index using the indexOf method.
You can use querySelectorAll to more easily grab the elements needed to do the work.
Note: querySelectorAll returns an HTMLCollection, not an Array. They both have a forEach method so I just wanted to point this out.
// get all anchor elements within an element with the class dropdown
let collection = document.querySelectorAll(".dropdown a");
// iterate over links in elements with dropdown class
// parameters for forEach are (element, index)
collection.forEach((ele, ind) => {
// we get the parent node(the element with the dropdown class)
// then we figure out what number element(div) we're on by looking at the array
// of all elements with the dropdown class
let p = ele.parentNode;
let p_ind = Array.from(document.querySelectorAll('.dropdown')).indexOf(p);
//we add 1 to the indices because there is a 0 index and we
//would like to start at the first DIV/Link being a 1, not 0
ind++;
p_ind++;
//add an event listener to the links that alerts the results
//on click
ele.addEventListener('click', () => alert("link " + ind + " in div" + p_ind))
})
let collection = document.querySelectorAll(".dropdown a");
collection.forEach((ele, ind) => {
let p = ele.parentNode;
let p_ind = Array.from(document.querySelectorAll('.dropdown')).indexOf(p);
ind++;
p_ind++;
ele.addEventListener('click', () => alert("link " + ind + " in div" + p_ind))
})
div {
border: 1px solid black;
width: 75px;
margin: 10px;
padding: 10px;
}
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
From looking at your code, I'd suggest changing
document.getElementsByClassName('dropdown').getElementsByTagName('a')
with
dropdownDivs[i].getElementsByTagName('a')
I say this because document.getElementsByClassName('dropdown') will return an array again (an array you've already got by the way) rather than the element in question, which would be represented by
dropdownDivs[i]
I'm studying JavaScript basics and today I built a simple html page which let the user to add/remove a list item. Well, I think I could be there (I know that there are a lot of better solutions, but hey, I'm just learning).
// the function that adds a list item
function addListItem () {
var newLi = document.createElement("li");
newLi.className = "listItem";
// newLi.innerHTML = "<h3>List item</h3> <p>This is a simple list item</p>";
list.appendChild(newLi);
}
You can see full code here: https://jsfiddle.net/l_wel/cuvc0m5g/
The problem is: how you can see within the first function, I put a commented code. It inserts html content inside the new list item. Is there a better way to do it? I mean, what if i would the new list item to have the number of the list item into the ?
Something like that:
List item 1
List item 2
List item 3
etc.. etc..
I know I should use a counter, but I was not able to let the created list items to have all the original html content from the first list item without the need to rewrite it within the function.
Ok, sorry for my bad english and sorry if you think this is a very simple problem, but I tried for hours. I hope you understood what I'm trying to achieve. I think that without the comment it could work as well, depending on the project.
P.S.
I don't know jQuery yet, I wanted to solve this using vanilla js.
See if this works for you:
// store the list
var list = document.getElementById("list");
var number = 1;
// the function that adds a list item
function addListItem () {
number++;
var newLi = document.createElement("li");
newLi.className = "listItem";
newLi.innerHTML = "<h3>List item</h3> <p>This is a simple list item " + number + "</p>";
list.appendChild(newLi);
}
// the function that removes the last list item
function removeListItem () {
number--;
var ulList = document.querySelectorAll("listItem");
var lastLi = list.lastElementChild;
var containerLi = lastLi.parentNode;
containerLi.removeChild(lastLi);
}
// add a list item
var btnAdd = document.getElementById("btnAdd");
if(btnAdd.addEventListener) {
btnAdd.addEventListener("click", addListItem, false);
} else {
btnAdd.attachEvent("click", addListItem, false);
}
// remove the last list item
var btnRemove = document.getElementById("btnRemove");
if(btnRemove.addEventListener) {
btnRemove.addEventListener("click", removeListItem, false);
} else {
btnAdd.attachEvent("click", removeListItem, false);
}
body {
font-family: monospace;
background: #1e2530;
color: #cce8ff;
}
.top { text-align: center; }
#list {
list-style-type: none;
padding: 0;
margin: 10px;
}
.listItem {
background: #cce8ff;
color: #1e2530;
margin-bottom: 10px;
padding: 5px 0 5px 10px;
border-radius: 5px;
}
<body>
<div class="top">
<h2>challenge #8</h2>
<button id="btnAdd">Add an item list</button>
<button id="btnRemove">Remove an item list</button>
</div>
<ul id="list">
<li class="listItem">
<h3>List item</h3>
<p>This is a simple list item 1</p>
</li>
</ul>
</body>
addListItem is a function which can accept parameters. for example, the forEach command is iterating the array and calling the addListItem for each of the items, forEach is calling the callback with two arguments, the first argument is the item itself, and the second is the index of the item in the array...
then you can use the arguments to display the data...
var items = ['Dog','Cat','Mouse'];
function addListItem( title, index ) {
var newLi = document.createElement("li");
newLi.className = "listItem";
newLi.innerHTML = "<h3>"+title+"</h3> " + index;
list.appendChild(newLi);
}
items.forEach( addListItem );
I know you said you didn't want to use JQuery (http://api.jquery.com/append/), but it does make your life easier. For example, you could use the function below. Writing JavaScript is fun, but reading good open source JavaScript (like reading JQuery source) is a far better learning experience.
you are going to need to create a counter to get the list number:
var lst = $('ul.mylist') //class is my list, if ul.mylist doesn't exist use append to append it to the document
for(let i = 0; i < [number of elements]; i++) {
lst.append('<li>List Item' + i + '</li>);
}