How to use closest() in a proper way in js - javascript

Student here!
Lets say i have function append() which generates <li> items inside an <ol>,those li items contain 2 buttons,one for removing the <li> that lies within and one for creating the same item but inside itself this time,in order to make another list layer.
Both by using the closest() method.
i cant figure out how to use the ADD <button>,i can call it but i cannot it make it work the way i want to.
I get this :
But i want to get something like this :
this is how i'm trying to do it :
function append() {
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.innerHTML = (`LIST ITEM <input class=input><button class=add>ADD</button><button class=remove>REMOVE</button>`);
ol.append(li)
document.getElementById("ol1").addEventListener("click",function(e) {
const tgt = e.target;
if (tgt.classList.contains("remove")) tgt.closest("li").remove();
if (tgt.classList.contains("add")) tgt.closest("li").appendChild(li);
})
}
<html>
<body>
<button id="btn1" onclick="append()">Append</button>
<ol id="ol1">
</ol>
</body>
</html>

To number with 1., 1.1, 1.2, 2., 2.1, 3., you need to use CSS. The counters function, obtains the number of the li. Each time an ol appears, the counter is reset to 1.. When a li appears, the top counter is used, concatenated with the new next new number of the previous li.
ol {
counter-reset: item
}
li {
display: block;
}
li:before {
content: counters(item, ".") ". ";
counter-increment: item;
}
In the append function, we add the li... For this, we call another function, which we will call create_li() which does the creation of the li.
function append() {
document.querySelector("#ol1").append( create_li() )
}
In the create_li() function, we create the li and return it with return li. In li, we add the two button elements, add and remove, but instead of doing it through a string, we do it with the function we already know, that is to say, document.createElement(), and also, on each button, we can add una función que llamaremos button_click function, used to receive the click event through addEventListener.
function create_li(){
var li = document.createElement("li")
var add = document.createElement("button")
var remove = document.createElement("button")
li.innerHTML = "LIST ITEM <input class=input>"
add.className = "add"
remove.className = "remove"
add.innerHTML = "+"
remove.innerHTML = "-"
add.addEventListener("click",button_click)
remove.addEventListener("click",button_click)
li.appendChild(add)
li.appendChild(remove)
return li
}
The button_click function, what it does is create the ol and li structure. In addition, it detects if the button clicked is the add or remove.
function button_click(e) {
const tgt = e.target;
var litg = tgt.closest("li")
var oltg = litg.querySelector("ol")
if(oltg==null){
var ol = document.createElement("ol")
litg.appendChild(ol)
oltg = ol
}
if (tgt.classList.contains("remove")){
litg.remove()
}
if (tgt.classList.contains("add")){
oltg.appendChild( create_li() )
}
}
The HTML structure is based on the li has to be inside the ol, and each sublist has to have an ol inside the li.
<ol>
<li>
1.
<ol>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ol>
</li>
<li>
2.
<ol>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ol>
</li>
<li>
3.
<ol>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ol>
</li>
</ol>
Finished code:
function button_click(e) {
const tgt = e.target;
var litg = tgt.closest("li")
var oltg = litg.querySelector("ol")
if(oltg==null){
var ol = document.createElement("ol")
litg.appendChild(ol)
oltg = ol
}
if (tgt.classList.contains("remove")){
litg.remove()
}
if (tgt.classList.contains("add")){
oltg.appendChild( create_li() )
}
}
function create_li(){
var li = document.createElement("li")
var add = document.createElement("button")
var remove = document.createElement("button")
li.innerHTML = "LIST ITEM <input class=input>"
add.className = "add"
remove.className = "remove"
add.innerHTML = "+"
remove.innerHTML = "-"
add.addEventListener("click",button_click)
remove.addEventListener("click",button_click)
li.appendChild(add)
li.appendChild(remove)
return li
}
function append() {
document.querySelector("#ol1").append( create_li() )
}
ol {
counter-reset: item
}
li {
display: block;
}
li:before {
content: counters(item, ".") ". ";
counter-increment: item;
}
<button id="btn1" onclick="append()">Append</button>
<ol id="ol1"></ol>

This is probably what you want?
function append() {
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.innerHTML = (`LIST ITEM <input class="input"><button class="add" onclick="add(this)">ADD</button><button class="remove" onclick="remove(this)">REMOVE</button><ol></ol>`);
ol.append(li)
}
function add(e) {
var li = document.createElement("li");
li.innerHTML = (`LIST ITEM <input class="input"><button class="add" onclick="add(this)">ADD</button><button class="remove" onclick="remove(this)">REMOVE</button><ol></ol>`);
e.parentElement.getElementsByTagName("ol")[0].appendChild(li);
}
function remove(e) {
e.parentElement.parentElement.removeChild(e.parentElement);
}
<html>
<body>
<button id="btn1" onclick="append()">Append</button>
<ol id="ol1">
</ol>
</body>
</html>

Instead of using 'closest' in the add method, you can simply find the parent and append in that.
EG : tgt.parentNode().appendchild(childLi)

Related

How to remove <li> list items and make the remaining ones change their position and id?

Tricky question and far too advanced for my level (js student).
Lets say I use the append method to generate <li> items inside an <ol>, and I need each one of those li items to have a unique id, so I thought to get the amount of <li> (length) items I generated by saying this : var index = document.getElementById("ol1").getElementsByTagName("li").length+1; and use this number to create unique id's for every item I generate by doing this: li.id="li"+index; so the first one I generate becomes #li1 (since the amount of li items is one), the next one #li2 and so on. *btw, is it the right approach to do this?
Now lets say I want to remove #li1, then #li2 would replace it in position 1 of the list, but its id will still be #li2 since it has gotten it already.
For example what I ultimately want is when I remove #li1, then #li2 becomes #li1, #li3 becomes #li2, #li4 becomes #li3.....and so on.
What would be the right logic approach to do such a gimmick?
function append() {
var index = document.getElementById("ol1").getElementsByTagName("li").length + 1;
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.id = "li" + index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li)
}
function remove1() {
var rem = document.getElementById("li1");
rem.remove();
}
function displayIndex() {
var index = document.getElementById("ol1").getElementsByTagName("li").length;
alert(index);
}
#li1 {
color: red;
}
#li2 {
color: green;
}
#li3 {
color: blue;
}
ol button {
color: red;
visibility: hidden;
}
#button1 {
color: red;
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<button id="btn1" onclick="append()">Append</button>
<button id="btn2" onclick="displayIndex()">Index</button>
</head>
<body>
<ol id="ol1">
</ol>
</body>
</html>
First off:
There are no "right" approaches. Only working implementations. How it is achieved might differ from developer to developer, and as long as it works, it should be considered the "right" way (however, not necessarily the most efficient or optimized or any other way).
One way would be to use a remove() function that re-assigns the ID's of the remaining <li>s (after the removal, obviously) as a side-effect.
function removeItemOf(list, listItem) {
if (!list.contains(listItem)) return;
listItem.remove();
indexItemsOf(list);
// For displaying the removed ID on-screen
document.querySelector('div').append(
document.createElement('br'),
document.createTextNode(`Removed <li> with id '${listItem.id}'`)
);
}
function indexItemsOf(list) {
for (var i = 0; list.children[i]; ++i) {
list.children[i].id = (list.id || list.tagName) + '-li' + i;
// For displaying the <li>'s ID on-screen
list.children[i].textContent = `With ID '${list.children[i].id}'`;
}
}
var list = document.querySelector('ol');
indexItemsOf(list);
setTimeout(() => {
removeItemOf(list, list.children[0]);
}, 2000);
setTimeout(() => {
removeItemOf(list, list.children[1]);
}, 4000);
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
<div></div>
However, re-assigning a previously used ID makes it not unique to one element, as it now identifies another element it hasn't identified before. For environments where this would be important (e.g. relying on an element to reference certain other elements, or to have a certain event-listener, etc.), re-using an ID would break the environment.
To circumvent this problem, one could keep track of how many unique items a list had over its entire lifetime, and create the next item with an ID using that amount, and then increase the amount by one.
Here is an example:
var list = document.querySelector('ol');
list.uniqueItems = 0;
// Here: Using Event-Delegation for removing a <li>
list.addEventListener('click', function(evt) {
if (evt.target.classList.contains('btn-delete'))
evt.target.closest('li').remove();
});
document.querySelector('button').addEventListener('click', function() {
var item = document.createElement('li');
item.id = 'li-' + list.uniqueItems;
item.textContent = `Has the ID '${item.id}' `;
var btnDelete = document.createElement('button');
btnDelete.classList.add('btn-delete');
btnDelete.textContent = 'Delete Item';
item.append(btnDelete);
list.append(item);
list.uniqueItems++;
});
<button>New Item</button>
<ol></ol>
You can just loop through each list item after your remove one and regenerate the IDs for each.
ol = document.getElementById("ol1");
function indexLIs(){
i = 1;
ol.querySelectorAll("li").forEach(function(li){
id = "li" + i;
li.setAttribute("id",id);
li.querySelector("input").value = "this is id: #" + id;
i++;
});
}
function append() {
var index = document.getElementById("ol1").getElementsByTagName("li").length + 1;
var li = document.createElement("li");
li.id = "li" + index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li)
}
function remove1() {
var rem = document.getElementById("li1");
rem.remove();
indexLIs();
}
function displayIndex() {
var index = document.getElementById("ol1").getElementsByTagName("li").length;
alert(index);
}
#ol1 li:nth-child(1) {
color: red;
}
#ol1 li:nth-child(2) {
color: green;
}
#ol1 li:nth-child(3) {
color: blue;
}
ol button {
color: red;
visibility: hidden;
}
#button1 {
color: red;
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<button id="btn1" onclick="append()">Append</button>
<button id="btn2" onclick="displayIndex()">Index</button>
</head>
<body>
<ol id="ol1">
</ol>
</body>
</html>
Your approach is fine. It would be easier for you to keep counting from the last element without changing the other elements even if you remove an element. In that situation use a global variable and initialize it with the length of your initial list. Something like that should do the work:
var index = 1; // or the index you wish to start from
function append(){
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.id="li"+index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li);
// now we will increment our index for the next iteration
index++;
}
Hope this was useful.

How can I set button content from script?

I'm trying to make todolist where I can add new tasks to do with same functionality as default ones (clickable task with 'delete' button next to it). My current code is creating new button, but it's only empty, grey rectangle, without any content (but it works as it should - it's deleting selected task) - how can I set content of new buttons as "delete"?
SNIPPET
function createListElement() {
var li = document.createElement("li");
var span = document.createElement("span");//create span
var button1 = document.createElement("button");//create button
span.appendChild(document.createTextNode(input.value)); //assign value to span
span.classList.add("complited"); //add class "complited" to span
li.appendChild(span); //put span as li child
ul.appendChild(li); // put li as ul child
li.appendChild(button1);
button1.classList.add("buttonli");
button1.value("Delete"); // here I've got error "button1.value is not a function"
li.classList.add("clickable"); //asign "clickable" class to li
input.value = "";
}
<ul id="ul1">
<li class="bold red clickable" random="23"><span class="complited">Notebook</span><button class="buttonli">Delete</button></li>
<li class="clickable"><span class="complited">Jello</span> <button class="buttonli">Delete</button></li>
<li class="clickable"><span class="complited">Spinach</span> <button class="buttonli">Delete</button></li>
<li class="clickable"><span class="complited">Rice</span> <button class="buttonli">Delete</button></li>
<li class="clickable"><span class="complited">Birthday Cake</span> <button class="buttonli">Delete</button></li>
<li class="clickable"><span class="complited">Candles</span> <button class="buttonli">Delete</button></li>
</ul>
button1.value("Delete"); // here I've got error "button1.value is not a function"
No, it's a simple accessor property.
button1.value = "Delete"
… but it sets the value that will be submitted when the button is used to submit a form. It doesn't set the display text, which is the content inside the button element.
That's why you wrote:
<button class="buttonli">Delete</button>
and not:
<button class="buttonli" value="Delete"></button>
So create a text node and put it inside the button:
const button_text = document.createTextNode("Delete");
button1.appendChild(button_text);
function createListElement() {
var li = document.createElement("li");
var span = document.createElement("span"); //create span
var button1 = document.createElement("button"); //create
var ul = document.getElementById("ul1"); //ul
var input = document.createElement("input"); //ul
span.appendChild(document.createTextNode(input.value)); //assign value to span
span.classList.add("complited"); //add class "complited" to span
li.appendChild(span); //put span as li child
ul.appendChild(li); // put li as ul child
li.appendChild(button1);
button1.classList.add("buttonli");
button1.innerHTML = "Delete"; //Because you are using a <button> instead of <input type="button">, you need to set the innerHTML
li.classList.add("clickable"); //asign "clickable" class to li
input.value = "";
}
That is because you are using a instead of therefore you need to set the innerHTML.
Check this answer here
Check the fiddle bellow.
Jsfiddle with solution

Appending elements through JavaScript for dynamically generated id

<li class="list" id='+id+'>
<a class="anchor" id='+id+' href='#stay' onClick="getValue(this);" style="text-decoration: none, color:red">hello</a>
Now inside JavaScript:
listElem=listElem+'<li class="list" style="height: 18px; display: inline- block;width:100px;line-height: 20px;padding-right:25px;"><a class="anchor" id=""+results+"" href="#stay" style="width:1028px;">"+results+"</a> </li>';
Now I need to append the listElem after li tag. Since id is dynamically generated.
How could I append it through innerHTML or html()?
How could I pass id here: $( "" ).html(listElem); or is here any other method?
You're probably looking for jQuery's insertAfter() and prop() methods.
If, say you have your HTML code in a a variable listItem, you can convert it to jQuery object, use prop() to set id and then insertAfter() to place it the last <li> element with a class="list":
var listElem = jQuery('<li class="list" style="height: 18px; display: inline-block;width:100px;line-height: 20px;padding-right:25px;"><a class="anchor" id=""+results+"" href="#stay" style="width:1028px;">"+results+"</a></li>');
listElem.prop('id', id).insertAfter('li.list:last');
If I understand your question correctly, then jQuery provides the before method of jQuery objects. To append listElem before the li element, you could call
$('#+id+').before(listElem);
you can do like this on click event
<li class="list" id='+id+' onclick='getvalue(this)'>
<a class="anchor" id='+id+' href='#stay' onClick="getValue(this);" style="text-decoration: none, color:red">hello</a>
jQuery code:
function getvalue(elem){
var listElem='<li class="list" style="height: 18px; display: inline- block;width:100px;line-height: 20px;padding-right:25px;"><a class="anchor" id=""+results+"" href="#stay" style="width:1028px;">"+results+"</a> </li>';
var id=$(elem).attr('id');
$('#'+id).html(listElem);//your code
}
Here is an option:
var ul = document.querySelector('ul');
var li1 = makeLi();
var li2 = makeLi();
var li3 = makeLi();
insert(ul, li2); // insert #my-uid-2
insert(ul, li1); // insert #my-uid-1
insert(ul, li3); // insert #my-uid-3
function uid () {
var i = 1;
return (this.uid = function () {
return 'my-uid-' + (i++);
})();
};
function makeLi () {
var re = /\+id\+/g;
var ul = document.createElement('ul');
var li = '<li id="+id+">#+id+</li>';
return (this.makeLi = function () {
ul.innerHTML = li.replace(re, uid());
return ul.firstChild;
})();
}
function insert (ul, li) {
var child;
var i = 0;
var re = /^.*?(\d+)$/;
var n = +li.id.replace(re, '$1');
var children = ul.childNodes;
while (
(child = children[i]) &&
n > +child.id.replace(re, '$1')
) i++;
if (child) ul.insertBefore(li, child)
else ul.appendChild(li);
}
#my-uid-1 {
background: yellow;
}
#my-uid-2 {
background: orange;
}
#my-uid-3 {
background: red;
}
<ul></ul>

how to add new <li> to <ul> onclick with javascript

How do I add a list element to an existing ul using a function from an onclick? I need it to add to this type of list ...
<ul id="list">
<li id="element1">One</li>
<li id="element2">Two</li>
<li id="element3">Three</li>
</ul>
... another list item with the id "element4" and text "Four" under that. I tried this function but it doesn't work...
function function1() {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode("Element 4"));
}
I don't know JQuery so Javascript only please. Thank you!!
You have not appended your li as a child to your ul element
Try this
function function1() {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode("Four"));
ul.appendChild(li);
}
If you need to set the id , you can do so by
li.setAttribute("id", "element4");
Which turns the function into
function function1() {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode("Four"));
li.setAttribute("id", "element4"); // added line
ul.appendChild(li);
alert(li.id);
}
You were almost there:
You just need to append the li to ul and voila!
So just add
ul.appendChild(li);
to the end of your function so the end function will be like this:
function function1() {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode("Element 4"));
ul.appendChild(li);
}
First you have to create a li(with id and value as you required) then add it to your ul.
Javascript ::
addAnother = function() {
var ul = document.getElementById("list");
var li = document.createElement("li");
var children = ul.children.length + 1
li.setAttribute("id", "element"+children)
li.appendChild(document.createTextNode("Element "+children));
ul.appendChild(li)
}
Check this example that add li element to ul.
Just use innerHTML:
function function1() {
ul.innerHTML += `<li> four </li>`;
}

How check in javascript that a li element has inner ul?

Good day.
HTML:
<ul>
<li class="sub">catalog
<ul>
<li class="dir">subcatalog
<ul>
<li>sublink</li>
<li>sublink</li>
<li>sublink</li>
</ul>
</li>
<li class="dir">subcatalog</li>
<li class="dir">subcatalog</li>
</ul>
</li>
<li class="sub">catalog</li>
<li class="sub">catalog</li>
<li class="sub">catalog</li>
<li class="sub">catalog</li>
</ul>
<style>
ul > li.sub:hover > ul{display:block;}
ul > li.sub:hover{
background: #fff url(../../images/arrow1.png) no-repeat 91% center;
border-bottom: 2px solid #e30613;
padding-right: 25px;
}
</style>
I use script:
$('li.main_menu_top_li').has('ul').addClass('sub');
Tell me please how make it on javascript (only javascript)?
One of two ways.
first get the element:
var d = document.getElementByClassName("li.main_menu_top_li");
Then:
if (d.firstChild) {
// It has at least one
d.className = d.className + " sub";
}
or the hasChildNodes() function:
if (element.hasChildNodes()) {
// It has at least one
d.className = d.className + " sub";
}
Assuming you want to add the sub class to the LI that has nested ULs under it:
Plain JS
var li = document.querySelector('li.main_menu_top_li');
if (li.getElementsByTagName("ul").length>0) li.className+="sub";
// or get the addClass/hasClass from http://snipplr.com/view/3561/
jQuery:
var li = $('li.main_menu_top_li');
if (li.children('ul').length) li.addClass('sub');
If you want to add the class to any UL that is inside an LI then
var li = $('li.main_menu_top_li');
li.children('ul').each(function() {
$(this).addClass('sub');
});
The following code looks at all LI elements and checks if they have a UL element. If so, that UL element gets the class 'sub'. If you want instead for that LI to have the class 'sub', un-comment the commented portions of code and then remove the line underneath each commented line.
Here's the first jsfiddle:
//IE 10+ and all other major browsers
var el = document.getElementsByTagName('li');
var listItems = Array.prototype.slice.call(el);
listItems.forEach(function(el) {
var childrenList = Array.prototype.slice.call(el.children);
childrenList.forEach(function(el) {
if(el.tagName === 'UL') {
//if(!el.parentNode.classList.contains('sub') {
if(!el.classList.contains('sub')) {
//el.parentNode.classList.add('sub')
el.classList.add('sub');
}
}
});
});
Here's the second jsfiddle:
//IE8+ and all other major browsers, I think
var el = document.getElementsByTagName('li');
var listItems = Array.prototype.slice.call(el);
listItems.forEach(function(el) {
var childrenList = Array.prototype.slice.call(el.children);
childrenList.forEach(function(el) {
if(el.tagName === 'UL') {
//if(el.parentNode.className === '') {
if(el.className === '') {
//el.parentNode.className = 'sub'
el.className = 'sub';
} else {
//el.parentNode.className += 'sub'
el.className += ' sub';
}
}
});
});

Categories

Resources