Dynamically creating collapsible lists without Jquery - javascript

I am trying to create an HTML list of items dynamically but I cannot understand how to make the lists collapsible. Right now I only have a way to dynamically create list items. Also if anyone has suggestions for code optimization they are always welcome. I am limited to just Javascript and HTML though. Sample of what the list will look like. The data is very long so I would like to be able to minimize scriptlog's list item on click.
var self = this;
var nameItem = document.createElement("li");
var dateItem = document.createElement("li");
nameNode = document.createTextNode("Name: " + session.name);
dateNode = document.createTextNode("Date: " + session.date);
nameItem.appendChild(nameNode);
dateItem.appendChild(dateNode);
var element = document.getElementById("sessionData");
element.appendChild(nameItem);
element.appendChild(dateItem);
session.actions.forEach(function(action, index) {
console.log(action);
var listItem = document.createElement("li");
var node = document.createTextNode(action.name);
var nestedUl = document.createElement("ul");
var dataUl = document.createElement("ul");
var scriptUl = document.createElement("ul");
var versionUl = document.createElement("ul");
var scriptLi = nestedUl.appendChild(document.createElement("li"));
var dataLi = nestedUl.appendChild(document.createElement("li"));
var versionLi = nestedUl.appendChild(document.createElement("li"));
var scriptInfo = scriptUl.appendChild(document.createElement("li"));
var dataInfo = dataUl.appendChild(document.createElement("li"));
var versionInfo = versionUl.appendChild(document.createElement("li"));
scriptLi.appendChild(document.createTextNode(action.script));
dataLi.appendChild(document.createTextNode(action.data));
versionLi.appendChild(document.createTextNode(action.version));
scriptInfo.appendChild(document.createTextNode(action.scriptTxt));
dataInfo.appendChild(document.createTextNode(action.dataTxt));
versionInfo.appendChild(document.createTextNode(action.versionTxt));
scriptLi.appendChild(scriptUl);
dataLi.appendChild(dataUl);
versionLi.appendChild(versionUl);
listItem.appendChild(node);
listItem.appendChild(nestedUl);
var element = document.getElementById("actionData");
element.appendChild(listItem);
});

A simple solution would be to add a click event listener to your <li> tags and set the display to 'none' for the child <ul> tags.
listItem.addEventListener("click", function() {
var ul = document.getElementById('ul-' + action.name);
ul.style.display = ul.style.display === '' ? 'none' : '';
});
See plnkr

You can update the style.display attribute of an element to show or hide it. As CDelaney noted, this can be done inside a click event listener.
If you already have the target li and ul elements in variables within a JavaScript closure, you can reference them directly from within the inline addEventListener function without needing to do any lookups to IDs or HTML properties.
Check out the example snippet below to see it in action.
var source = {
text: "1",
items: [
{text: "1A"},
{text: "1B", items: [
{text: "1B-i"},
{text: "1B-ii"},
{text: "1B-iii"},
{text: "1B-iv", items: [
{text:"1B-iv-a"},
{text:"1B-iv-b"},
{text:"1B-iv-c"}
]}
]},
{text: "1C", items:[
{text:"1C-i"},
{text:"1C-ii"},
{text:"1C-ii"}
]},
{ text: "1D"}]
};
var rootNode = document.getElementById("actionData");
createListFromObject(source, rootNode);
function createListFromObject(obj, element) {
if (obj.text) { // add text node
var li = document.createElement("li");
li.appendChild(document.createTextNode(obj.text));
element.appendChild(li);
}
if (obj.items) { // add child list
var ul = document.createElement("ul");
element.appendChild(ul);
if (li) {
li.style.cursor = "pointer";
li.style.color = "blue";
li.style.textDecoration = "underline";
// make clicking the text node toggle the child list's visibility:
li.addEventListener("click", function() {
ul.style.display = ul.style.display === "none" ? "inherit" : "none";
});
}
for (var i = 0, len = obj.items.length; i < len; i++) {
createListFromObject(obj.items[i], ul);
}
}
}
<ul id="actionData"></ul>

Related

How to use delegate in javascript

I try to make todo list app with javascript, I have function to create new each item (li elements) like that
todoView: function (todo) {
var controller = todoController.todoList;
console.log('controller', controller);
var item = document.createElement('li');
item.setAttribute('class', 'todoItem');
var inpCheckbox = document.createElement('input');
this.setAttributes(inpCheckbox, { 'type': 'checkbox', 'class' : 'itemList'} );
var lbContent = document.createElement('label');
lbContent.innerHTML = todo.getContent();
var btnRemove = document.createElement('button');
console.log('id', this.id)
this.setAttributes(btnRemove, { 'class': 'remove', 'id' : this.id} );
// btnRemove.setAttribute('id', this.id);
//item append each element
item.appendChild(inpCheckbox);
item.appendChild(lbContent);
item.appendChild(btnRemove);
console.log('item', item);
//ul append each item
document.querySelector('#todoListView').appendChild(item);
},
but in the end file I set the handle event for all li item have class .remove
var deleteItem = document.getElementsByClassName('remove')
for(var i = 0; i < deleteItem.length; i++) {
deleteItem[i].addEventListener("click", function() {
todoController.removeTodo(i);
});
}
But now I have issue that I can't call .remove class, because it just create into todoView function. It to be contain difference block. In jquery I can use delegate to resolve it, but I don't know how to fix it in js pair. Pls help me
Instead of
var deleteItem = document.getElementsByClassName('remove')
for(var i = 0; i < deleteItem.length; i++) {
deleteItem[i].addEventListener("click", function() {
todoController.removeTodo(i);
});
}
you'll want
todoListView.addEventListener('click', function(e) {
if(e.target.classList.contains('remove')){
todoController.removeTodo(e.target);
}
});
However, I think your removeTodo function is flawed

how to create new list and add list items to new list using javascript

I am trying to create a new list and add it to the DOM, and then add list items to new list along with text node to each list item.
This is what I have so far, after trying several ways to do this, but still not accomplishing goal. any help is appreciated.The first 4 lines of code is HTML snippet, the code below that is the JavaScript code. Again thank you for any help with this.
<body>
<div id="nav"></div>
<script src="js/script.js"></script>
</body>
var newList = document.createElement("ul");
var newListItem = document.createElement("li");
var stringArray = ["Home","About","Our Services","Contact Us"];
var newUL = document.getElementById("nav").appendChild(newList);
function buildList(){
for( var i = 0; i < stringArray.length; i++){
newUL.appendChild(newListItem);
}
var listItems = document.getElementsByTagName("li");
for( var i = 0; i < listItems.length; i++){
listItems[i].appendChild(stringArray[i]);
}
}
buildList();
Two problems:
You're appending the same li over and over. You need to create a new one for each item.
You can't append a string to a DOM element, but you can set its textContent:
var stringArray = ["Home","About","Our Services","Contact Us"];
function buildList(){
var newList = document.createElement("ul");
var newListItem;
document.getElementById("nav").appendChild(newList);
for( var i = 0; i < stringArray.length; i++){
newListItem = document.createElement('li');
newListItem.textContent = stringArray[i];
newList.appendChild(newListItem);
}
}
buildList();
<div id="nav"></div>
Slightly cleaner version with .forEach():
var stringArray = ["Home","About","Our Services","Contact Us"];
function buildList(){
var newList = document.createElement("ul");
document.getElementById("nav").appendChild(newList);
stringArray.forEach(function (title) {
var newListItem = document.createElement('li');
newListItem.textContent = title;
newList.appendChild(newListItem);
});
}
buildList();
<div id="nav"></div>
You need to create a text node and append it to the <li> element.
var newList = document.createElement("ul");
var stringArray = ["Home","About","Our Services","Contact Us"];
// Create a <ul> element
var newUL = document.getElementById("nav").appendChild(newList);
function buildList(){
for(var i = 0; i < stringArray.length; i++){
// Create a text node
var newTextNode = document.createTextNode(stringArray[i]);
// Create a list element
var newListItem = document.createElement("li");
// Append text node and list item
newListItem.appendChild(newTextNode);
newUL.appendChild(newListItem);
}
}
buildList();
<body>
<div id="nav"></div>
</body>
Just loop over the string array and add lis, like this:
var nav = document.querySelector("nav");
var list = document.createElement("ul");
var items = ["Home","About","Our Services","Contact Us"];
items.forEach(function(item) {
var li = document.createElement("li");
li.innerText = item;
list.appendChild(li);
})
nav.appendChild(list);
Codepen example here
If it's supposed to be a site navigation, you may want to add links. That's easy, too – just append <a> in the loop like this:
var nav = document.querySelector("nav");
var list = document.createElement("ul");
var items = [{
text: "Home",
url: "/home"
}, {
text: "About",
url: "/about"
}, {
text: "Our services",
url: "/services"
}, {
text: "Contact Us",
url: "/contact"
}]
items.forEach(function(item) {
var li = document.createElement("li");
var link = document.createElement("a");
link.innerText = item.text;
link.href = item.url;
li.appendChild(link);
list.appendChild(li);
})
nav.appendChild(list);
Codepen example here
In this case, I would contend that using innerHTML and Array#join is simpler and more readable than other alternatives:
var stringArray = ["Home", "About", "Our Services", "Contact Us"];
function buildList() {
document.getElementById('nav').innerHTML = '<ul><li>' + stringArray.join('</li><li>') + '</li></ul>'
}
buildList()
<nav id="nav"></nav>

Delete unordered list in HTML with Javascript DOM

Basically, the task I needed to perform is to delete the whole "agent" array by using the removeChild() function, but it needs the <ul> id, according to what I read [here][1].
After that, I need to replace the "agent" array with "freshagent" array and display it in an unordered list.
My problem is that I can't set the id for "ul" because I used the createElement() function.
Thus, I can't remove the "agent" array.
//old code
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="wrapper">
</div>
<script>
var agents = [
"Dion" ,
"Elisa" ,
"Selena" ,
"Shauna" ,
"Rodger"
];
var freshAgents = [
"Gray",
"Shivani",
"Brainne",
"Jason",
"FChad"
];
var i = 0;
var ul = document.createElement('ul');
ul.setAttribute("id","aList");
for (i in agents){
var list = document.createElement('li');
var node = document.createTextNode(agents[i]);
list.appendChild(node);
ul.appendChild(list);
}
rList = document.getElementById("aList");
if (rList.hasChildNodes()){
for(i in agents){
rList.removeChild(rList.childNodes[i]);
}
}
var finalList = agents.concat(freshAgents);
for (i in finalList){
var list = document.createElement('li');
list.appendChild(document.createTextNode(finalList[i]));
ul.appendChild(list);
}
document.getElementById("wrapper").appendChild(ul);
</script>
</body>
</html>
//new code(working)
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="wrapper">
</div>
<script>
var agents = ["Dion" ,"Elisa" , "Selena", "Shauna", "Rodger"];
var freshAgents = ["Gray", "Shivani", "Brainne", "Jason","FChad"];
var i = 0;
var ul = document.createElement('ul');
for (i in agents){
var list = document.createElement('li');
var node = document.createTextNode(agents[i]);
list.appendChild(node);
ul.appendChild(list);
}
var rList = document.getElementById("wrapper").appendChild(ul);
do{
rList.removeChild(rList.childNodes[0]);
}while(rList.hasChildNodes())
for (i in freshAgents){
var list = document.createElement('li');
list.appendChild(document.createTextNode(freshAgents[i]));
ul.appendChild(list);
}
</script>
</body>
</html>
Seems like you are going about it a really long way. I have tweaked your code somewhat to make it do what I think you are asking. I would do something different but it seems clear from your question that this is the way you want it.
var agents = ["Dion", "Elisa", "Selena", "Shauna", "Rodger"];
var freshAgents = ["Gray", "Shivani", "Brainne", "Jason", "FChad"];
var ul = document.createElement('ul');
ul.id = "aList";// give it an ID
for (var i = 0; i < agents.length; i++) {
var list = document.createElement('li');
var node = document.createTextNode(agents[i]);
list.appendChild(node);
ul.appendChild(list);
}
document.getElementById("wrapper")
.appendChild(ul);
rList = document.getElementById("aList");//this is the same as ul variable declared above
if (rList.hasChildNodes()) {
console.log(rList.childNodes);//lists the <LI>...<LI> nodes
for (var i = 0; i < rList.childNodes.length; i++) {//Loop through the nodes not your agent array
rList.removeChild(rList.childNodes[i]);
}
}
var finalList = agents.concat(freshAgents);
for (var i = 0; i < finalList.length; i++) {
var list = document.createElement('li');
list.appendChild(document.createTextNode(finalList[i]));
ul.appendChild(list);
}
Working fiddle here
I can't really understand much of your question, but based on this
Problem: I can't set id for "ul" because of using createelement function. I can't remove "agent" array.
I assume you want to add an id to an element that was created with document.createElement(). You can do that like so:
var el = document.createElement('div');
el.id = 'exampleID';
First of all, please input your code in a neat and legible manner, so that it is readable by everyone. I've formatted your code to the following:
var agents = ["Dion", "Elisa", "Selena", "Shauna", "Rodger"];
var freshAgents = ["Gray", "Shivani", "Brainne", "Jason", "FChad"];
var i = 0;
var ul = document.createElement('ul');
ul.setAttribute("id", "aList");
for (i in agents) {
var list = document.createElement('li');
var node = document.createTextNode(agents[i]);
list.appendChild(node);
ul.appendChild(list); }
rList = document.getElementById("aList");
if (rList.hasChildNodes()) {
for (i in agents) {
rList.removeChild(rList.childNodes[i]);
}
}
var finalList = agents.concat(freshAgents);
for (i in finalList) {
var list = document.createElement('li');
list.appendChild(document.createTextNode(finalList[i]));
ul.appendChild(list);
}
document.getElementById("wrapper").appendChild(ul);
Another tip: since you're referring your HTML, it's recommended that you provide that code as well. Again, it's not a necessity, but just etiquette.
As for setting the id, use:
var example = document.createElement('ul');
example.id = 'example_ID';

Hiding and showing classes with the same Class with jQuery

I'm creating a tool which generates a bunch of divs based on data I input into an array, however they all have the same class. The idea is that when one link is clicked it shows one of the ".catbox" divs and hides the rest.
All of these divs have the same class so I need to iterate through them, but I'm not quite sure how this is done with jQuery. Currently clicking on the last ".list" class triggers the on click event instead of all of them, and currently it shows all of the divs with the class ".catbox" instead of the corresponding one.
Here is the code:
var HTMLcatName = '<h1>%data%</h1>';
var HTMLcatImage = '<img id="cat" src="%data%">';
var HTMLcatCounter = '<p class="counter">Number of clicks: %data%</p>';
var HTMLcatList = '<p>%data%</p>'
var noCats = 'No cats selected m8';
var getCounterClass = document.getElementsByClassName("counter");
$(document).ready(function() {
cats.display();
$('.catbox').hide();
for (u = 0; u < cats.name.length; u++) {
formattedCatList = HTMLcatList.replace("%data%", cats.name[u]);
var listDiv = document.createElement('div');
listDiv.innerHTML = formattedCatList;
listDiv.className = "list";
$(".list").click(function() {
$(".catbox").toggle("slow");
});
$("body").prepend(listDiv);
}
});
var update = function() {
for (j = 0; j < getCounterClass.length; j++) {
getCounterClass[j].innerHTML = 'Number of clicks: ' + cats.clicks[j];
}
}
var cats = {
"name": ["Monte", "Jib"],
"image": ["images/monte.jpg", "images/jib.jpg"],
"clicks": [0, 0],
display: function () {
for (i = 0; i < cats.image.length; i++) {
formattedCatNames = HTMLcatName.replace("%data%", cats.name[i]);
formattedCatImages = HTMLcatImage.replace("%data%", cats.image[i]);
formattedCatCounter = HTMLcatCounter.replace("%data%", cats.clicks[i]);
var catDiv = document.createElement('div');
catDiv.className = "catbox";
catDiv.innerHTML = formattedCatNames + formattedCatImages + formattedCatCounter;
catDiv.querySelector('img').addEventListener('click', (function(catCountUp) {
return function() {
cats.clicks[catCountUp]++;
update();
};
})(i));
document.body.appendChild(catDiv);
}
},
}
The function I need help with is found within $(document).ready(function() {
Any help would be greatly appreciated.
The following can do it:
$(".list").on("click", function(){
$(this).find(".catbox").toggle("slow");
});
With $('.list') you get a group of elements of class list, so if you use $('.list').click(); you will bind the click event to just one element. You should use:
$(".list").each(function(){
$(this).click(function() {
$(".catbox").toggle("slow");
});
});

Remove clicked <li> onclick

I have this JavaScript code:
window.onload = init;
function init () {
var button = document.getElementById("submitButton");
button.onclick = addItem;
var listItems = document.querySelectorAll("li"); //assigning the remove click event to all list items
for (var i = 0; i < listItems.length; i++) {
listItems[i].onclick = li.parentNode.removeChild(li);
}
}
function addItem() {
var textInput = document.getElementById("item"); //getting text input
var text = textInput.value; //getting value of text input element
var ul = document.getElementById("ul"); //getting element <ul> to add element to
var li = document.createElement("li"); //creating li element to add
li.innerHTML = text; //inserting text into newly created <li> element
if (ul.childElementCount == 0) { //using if/else statement to add items to top of list
ul.appendChild(li); // will add if count of ul children is 0 otherwise add before first item
}
else {
ul.insertBefore(li, ul.firstChild);
}
}
function remove(e) {
var li = e.target;
var listItems = document.querySelectorAll("li");
var ul = document.getElementById("ul");
li.parentNode.removeChild(li);
}
and this HTML:
<body>
<form>
<label for="item">Add an item: </label>
<input id="item" type="text" size="20"><br>
<input id="submitButton" type="button" value="Add!">
</form>
<ul id="ul">
</ul>
<p>
Click an item to remove it from the list.
</p>
</body>
What I want to do is remove the whichever <li> element the user clicks, but this doesn't seem to be working and I am unable to find an answer anywhere else online for this specific scenario. Hoping someone can help me out here and show me what i am missing.
UPDATE
Plain JS delegation
Add the eventListener to the UL to delegate the click even on dynamically inserted LIs:
document.getElementById("ul").addEventListener("click",function(e) {
var tgt = e.target;
if (tgt.tagName.toUpperCase() == "LI") {
tgt.parentNode.removeChild(tgt); // or tgt.remove();
}
});
jQuery delegation
$(function() {
$("#submitButton").on("click",function() {
var text = $("#item").val(); //getting value of text input element
var li = $('<li/>').text(text)
$("#ul").prepend(li);
});
$("#ul").on("click","li",function() {
$(this).remove();
});
});
Original answer
Since you did not mention jQuery
var listItems = document.getElementsByTagName("li"); // or document.querySelectorAll("li");
for (var i = 0; i < listItems.length; i++) {
listItems[i].onclick = function() {this.parentNode.removeChild(this);}
}
you may want to wrap that in
window.onload=function() { // or addEventListener
// do stuff to the DOM here
}
Re-reading the question I think you also want to add that to the dynamic LIs
li.innerHTML = text; //inserting text into newly created <li> element
li.onclick = function() {
this.parentNode.removeChild(this);
// or this.remove(); if supported
}
Here is the complete code as I expect you meant to code it
Live Demo
window.onload=function() {
var button = document.getElementById("submitButton");
button.onclick = addItem;
}
function addItem() {
var textInput = document.getElementById("item"); //getting text input
var text = textInput.value; //getting value of text input element
var ul = document.getElementById("ul"); //getting element <ul> to add element to
var li = document.createElement("li"); //creating li element to add
li.innerHTML = text; //inserting text into newly created <li> element
li.onclick = function() {
this.parentNode.removeChild(this);
// or this.remove(); if supported
}
if (ul.childElementCount == 0) { //using if/else statement to add items to top of list
ul.appendChild(li); // will add if count of ul children is 0 otherwise add before first item
}
else {
ul.insertBefore(li, ul.firstChild);
}
}
In case you want to use jQuery, the whole thing gets somewhat simpler
Live Demo
$(function() {
$("#submitButton").on("click",function() {
var text = $("#item").val(); //getting value of text input element
var li = $('<li/>')
.text(text)
.on("click",function() { $(this).remove()});
$("#ul").prepend(li);
});
});
I know you already received an answer, but back to your original remove function. You have the following:
function remove(e) {
var li = e.target;
var listItems = document.querySelectorAll("li");
var ul = document.getElementById("ul");
li.parentNode.removeChild(li);
}
Change it to this and you should get what you were trying to achieve:
function remove(e)
{
var li = e.target;
var ol = li.parentElement;
ol.removeChild( li);
return false;
}
I'd suggest simplifying things a little:
Object.prototype.remove = function(){
this.parentNode.removeChild(this);
};
var lis = document.querySelectorAll('li');
for (var i = 0, len = lis.length; i < len; i++) {
lis[i].addEventListener('click', remove, false);
}
JS Fiddle demo.
Of course, having done the above, I'd then have to go further (possibly because I like jQuery too much) and also:
Object.prototype.on = function (evt, fn) {
var self = this.length ? this : [this];
for (var i = 0, len = self.length; i < len; i++){
self[i].addEventListener(evt, fn, false);
}
};
Object.prototype.remove = function(){
var self = this.length ? this : [this];
for (var i = 0, len = self.length; i < len; i++){
self[i].parentNode.removeChild(self[i]);
}
};
document.querySelectorAll('li').on('click', remove);
JS Fiddle demo.
If you don't want to write function in javascript, you can use immediately invoked anonymous function like below...
<elem onclick="(function(_this){_this.parentNode.removeChild(_this);})(this);"
If I understood you correctly:
$("li").on("click", function() {
$(this).remove()
});
The answer is more obvious than it could seem, you forgot to add init() in your script, is normal that the click event aren't triggered, they're not set on the element!
EDIT:
Your code has some logical errors. If you don't add an onclick function for all those created elements you will not be able to remove the clicked element. This is because the function init() is called one time at the load of the page!
function init() {
var button = document.getElementById("submitButton");
button.onclick = function() {addItem()};
}
function addItem() {
var textInput = document.getElementById("item"); //getting text input
var text = textInput.value; //getting value of text input element
var ul = document.getElementById("ul"); //getting element <ul> to add element to
var li = document.createElement("li"); //creating li element to add
li.innerHTML = text; //inserting text into newly created <li> element
li.onclick = function() {this.parentNode.removeChild(this);}
if (ul.childElementCount == 0) { //using if/else statement to add items to top of list
ul.appendChild(li); // will add if count of ul children is 0 otherwise add before first item
} else {
ul.insertBefore(li, ul.firstChild);
}
}
init();

Categories

Resources