I'm having some trouble figuring out how to add elements to a parent object on a click event.
The result that i'm hoping to achieve is this:
<ul>
<li><button>B1</button>
<ul class="newul">
<li><button>B1.1</button>
<ul class="newul">
<li><button>1.1.1</button></li>
<li><button>1.1.2</button></li>
</ul>
</li>
<li><button>B1.1</button></li>
</ul>
</li>
<li><button>B2</button></li>
<li><button>B3</button></li>
</ul>
Let's say I click button B2. I want a new UL added to the parent LI of that button and then be able to add new LI elements to the newly created UL. I hope that makes sense!
So basically, click button, add new UL with class "newul" to the LI you're currently in -> add new LI's to that newly created UL.
The jquery I'm currently using is as follows:
$('button').click(function(){
//Get parent..
var parent = $(this).parent();
//Add a new UL to the parent and save it as newul
var newul = parent.add("ul");
//Add the class to the new UL
newul.addClass('newul');
//Add a new li to the new UL
newli = newul.add('li');
//Add a button to the new LI
newli.append('<button></button>');
});
Unfortunately, this is having completely undesired effects and sticks buttons everywhere all over the place. I'd appreciate any help you can offer.
Here's a visible example of what i'm after. The top part is the effect id like to achieve.
Example of desired result
Even though #am not i am has the correct code. There is no explanation of why your code fails, and I think that's the answer you are asking for.
There are several problems in your code:
First
//Add a new UL to the parent and save it as newul
var newul = parent.add("ul");
The 'ul' is a selector and so is going to search the DOM for other <ul> elements, rather than create a new one. Also, parent.add() method is returning the parent object, not the ul's that you selected.
Correct code:
//Add a new UL to the parent and save it as newul
var newul = $("<ul>").appendTo(parent);
Second:
//Add a new li to the new UL
newli = newul.add('li');
Same problem again, and since newul is actually still the parent you're getting all types of craziness. Also, you're missing a var, but maybe I just don't get your closure.
Correct code:
//Add a new li to the new UL
var newli = $("<li>").appendTo(newul);
That's all. If you fix that in your code, it'll work.
However, unless you really need those references to persist, better performance is usually achieved if you pass the whole thing as a string:
$('button').click( function() {
$(this).parent()
.append(
$('<ul class="newul"><li><button></li></ul>')
);
});
Edit
And if you wanted all the new buttons to have the same functionality, use the on() method and a class for your buttons:
$('body').on('click', 'button.ul-appending', function() {
$(this).parent()
.append(
$('<ul class="newul"><li><button class="ul-appending"></li></ul>')
);
});
You could change the 'body' selector to something much more specific so this doesn't get queried on every click on your page. Just make sure to add the class ul-appending to all the existing buttons that aren't generated by this handler.
JSFiddle
One issue I see here is that the new buttons created will not have the click event bound to them. I fix this by using on and setting the event as a delegate. In doing so I give the outer ul an id of container. I also check to make sure you haven't already added a ul element and append one if it isn't inside the li. Here is a working example.
$('#container').on("click", "button.newbtn", function(){
var parent = $(this).closest('li');
var childUl = parent.children('ul');
if(childUl.length === 0) {
parent.append("<ul class='newul'></ul>");
childUl = parent.children('ul');
}
childUl.append($("<li><button class='newbtn'>" + $(this).html() + "." + (childUl.children().length + 1) + "</button></li>"));
});
$('button').click(function(){
//Get parent..
var parent = $(this).parent();
//Add a new UL to the parent and save it as newul
var newul = $("<ul/>");
//Add the class to the new UL
newul.addClass('newul');
//Add a new li to the new UL
var newli = $('<li/>');
//Add a button to the new LI
newli.append('<button></button>');
// add ul to parent li
parent.append( newul.append( newli ) );
});
The issue here is that you're using .add() instead of .after().
.add() adds the passed element to the current selection. .after() adds the passed elements after the current selection.
Using .after(), we can simplify your script into a nice little one-liner:
$('button').click(function(){
$(this).after('<ul class="newul"><li><button></button></li></ul>');
});
Since .after() inserts content next to the current element, there's no need to get the .parent(). .after() can take a complete string of HTML, so there's no need to use jQuery to manipulate the class and add the child elements.
Example.
Use .on function for dynamically added elements.
You can use something like this.
$(document).ready(function(){
i = 1;
$('body').on("click","button",function(event){
$(this).after('<ul class="newul"><li><button>TestButton'+i+'</button></li></ul>');
i++;
});
});
Here is the DEMO. Adjust button with your css and button values accordingly.
Related
I just finished learning Javascript. Now i am doing some practical test to further sharpen my knowledge. I look for some random practical projects online and i found this site (https://skillcrush.com/2018/06/18/projects-you-can-do-with-javascript/) 9. Build a JavaScript To-Do List.
As i scan the codes, i understand how the flow works but got confused in this particular section:
*Everytime the createListElement() function is triggered, does it create an array of li element?
*How does the delete button created for every 'li' is only associated to work with that specific li element?
The createListElement() function creates a single list element. Here is what it does.
Creates the element.
Adds it to the list.
Adds the strike-through.
Adds the delete button.
The delete button is a child element of the list item. In it's onclick listener, it stores the specific li element that it belongs to. Perhaps more ideal code in that area would look something like this:
function deleteListItem(e) {
e.path[0].parentElement.classList.add("delete")
}
dBtn.addEventListener("click", deleteListItem)
In all onclick events, e.path[0] is the element that was clicked. Since the delete button has no children within it that will also trigger that function, it will always be the delete button. Then, by getting the parent element, it will always be the right list item.
Here is related code:
function createListElement() {
var li = document.createElement("li"); // creates an element "li"
li.appendChild(document.createTextNode(input.value)); //makes text from input field the li text
ul.appendChild(li); //adds li to ul
input.value = ""; //Reset text input
//START STRIKETHROUGH
// because it's in the function, it only adds it for new items
function crossOut() {
li.classList.toggle("done");
}
li.addEventListener("click",crossOut);
//END STRIKETHROUGH
// START ADD DELETE BUTTON
var dBtn = document.createElement("button");
dBtn.appendChild(document.createTextNode("X"));
li.appendChild(dBtn);
dBtn.addEventListener("click", deleteListItem);
// END ADD DELETE BUTTON
//ADD CLASS DELETE (DISPLAY: NONE)
function deleteListItem(){
li.classList.add("delete")
}
//END ADD CLASS DELETE
}
A1: While createListElement triggered, var li = document.createElement("li") would create a single li element, and append to DOM by ul.appendChild(li)
A2: var dBtn = document.createElement("button"); create a button element and append to li element by li.appendChild(dBtn).
dBtn.addEventListener("click", deleteListItem); add a click event to the corresponding button, if button clicked, it would trigger defined deleteListItem function. And in deleteListItem function:
function deleteListItem(){
li.classList.add("delete")
}
li refers to the 'li' element you just created. Thus it would add a delete class to associated li element and hide li.
*Everytime the createListElement() function is triggered, does it create an array of li element?
Close! Think of ul as the parent container of all the li items. In this specific example there is ul already defined globally:
var ul = document.querySelector("ul");
Everytime createListElemet() is triggered it creates a new li element and appends to the existing ul container.
How does the delete button created for every 'li' is only associated
to work with that specific li element?
If you follow the code line at a time -
var dBtn = document.createElement("button"); //create button element
dBtn.appendChild(document.createTextNode("X")); //create text node with text "X" and append to the button element
li.appendChild(dBtn); //append the deleteBtn + text node to the li element. Here is why that specific delete element is associated to that *li* element.
I'm trying to check if each element of an ul list is visible using the jquery.visible plugin. The problem is that the script does not handle each "li" element as independent, so putting this:
var element = $("ul li");
if (element.visible(true)) {
element.removeClass("hidden");
}
Removes the "hidden" class of all elements at the same time.
Any ideas?
You are initializing element as an array, so the name is misleading, and it may be throwing you off later in the code.
You want something like this (untested):
var arrElements = $("ul li");
arrElements.each(function() {
if ($(this).visible(true)) {
$(this).removeClass("hidden");
}
});
Note that I am using the each method and $(this) to act on only one li element at a time.
How about checking just the css property:
if(element.css('display') != 'none')
I'm trying to identify a li element from a group of li inside of div
<div id="group">
<li></li>
<li></li>
<li></li>
</div>
its quite simple tho, i could give each li an unique id and this problem would be over. like
var listItem1,2,3 = document.getElementById('liItem1,2,3') etc
listItem1,2,3.addEventListener('click',function);
this might be handy when it comes to 1,2 or 3 elements but this is all static and when it start to scale its not possible anymore, Instead im trying to make use of NodeList.
var nodeList = document.getElementById('group').getElementsByTagName('li');
now i will have a NodeList with li 0, li 1, li 2
the problem comes now becouse i don´t know how to trace which li is being clicked.
nodeList.addEventListener('click',function);
wont work here becouse it dosent know which one is being clicked at here.
nodeList[0].addEventListener('click',function);
is the same solution as above. How can i trace which of the li is being clicked at? only plain/raw javascript
To find the index of an element in response to an event, I'd suggest delegating the event-handling to an ancestor (rather than individually binding an event-handler to multiple child-elements):
// 'event' is passed in automagically (in non IE browsers, haven't tested IE):
function getIndexFrom(event){
// event.target is the element upon which the event was triggered:
var clicked = event.target,
// finding all the children of the parent of the clicked-element
// (could use 'this.children', as 'this' will be the 'ul' in this demo):
children = clicked.parentNode.children;
// iterating over those child elements:
for (var i = 0, len = children.length; i < len; i++){
// if the clicked element is the current element:
if (children[i] === clicked){
console.log('index is: ' + i)
// we return 'i' as the index:
return i;
}
}
// this shouldn't happen, assuming we're looking at the right group
// of elements, but it's there as an in-case and for debugging:
return false;
}
document.getElementById('group').addEventListener('click', getIndexFrom);
JS Fiddle demo.
References:
EventTarget.addEventListener().
for... loop.
ParentNode.children.
I want to find every specific LI element on a page, look through it for particular class, pull out that class, change it a bit and then set their values as variables.
There are up to 12 li items on a page at one time, and each have different values of the 'was' price. I want to grab that 'was' price and use within that li element only.
Here is what I have so far:
js:
jQuery(document).ready(function () {
jQuery("#productList ul.gridStyle li").each(function () {
var wasPriceMinusWasRemoval = jQuery('#productList p.was').text().replace(/\u00A3/g, '');
var wasPrice = wasPriceMinusWasRemoval.replace("Was ", "");
console.log(wasPrice); // ==> "149.99Was 119.99" (should be just 149.99)
// generate a specific tag using .attr() dependent on
// wasPrice value inside this li element
});
});
Question is, where to go from here? Use of index?
Try to use the this reference inside the .each() function,
var wasPriceMinusWasRemoval = jQuery('p.was',this).text().replace(/\u00A3/g, '');
i have the following code that adds data to unordered lists. The troublesome code is as follows:
$('li:not(:first-child)').click(function() {
var clickedLi = this;
$(this).parent().prepend(clickedLi);
var selectedLi = $(this).text();
alert(selectedLi);
$(this).parent().parent().nextAll().children('ul').children().replaceWith('<li>Please select</li>');
//ajax call
var returnedData = "<li>Dataset1</li><li>Dataset2</li><li>Dataset3</li>";
//populate next with returned data
$(this).parent().parent().next().children('ul').append(returnedData);
});
If you click on one of the options in the first ul, data gets added to the second ul. However, the list items under this ul are not clickable, and I need them to be so, so that 'chain' can be continued.
Where is the bug? Any help would be much appreciated. Thanks in advance.
Try with: (if you use jQuery greater than 1.6.8. )
$(document).on('click','li:not(:first-child)',function() {
http://api.jquery.com/on/
I can suppose, judging by your code, that you expect the freshly added li to inherit the behaviour you append to the existing ones. Unfortunately it cannot work with the code written that way.
You need to attach the click event in an "observable" way so newly added elements to the dom will get the event handler attached.
To do this, you must use the .on method. You can check its reference here