How to append <li> to a <ul> on a button click.
I have tried below script, but want to have a number appended to the end of <li>. Like item 0, item 1, item 2, etc..
var list = document.getElementById("list");
var add = document.getElementById('addElem');
add.addEventListener('click', function() {
var itemsByTagName = document.getElementsByTagName("li");
list.innerHTML += '<li>item</li>'
});
<ul id="list">
<li>item
</li>
</ul>
<button id="addElem">Add</button>
Here is a example to get started.
Note: For production use you might need to handle the variable i through server/session/local storage variable.
var i = 1;
var list = document.getElementById("list");
var add = document.getElementById('addElem');
add.addEventListener('click', function() {
var itemsByTagName = document.getElementsByTagName("li");
list.innerHTML += '<li>item ' + i++ + '</li>'
});
<ul id="list">
<li>item
</li>
</ul>
<button id="addElem">Add</button>
Related
I have the following markup:
<ul class="category-top-navigation__group category-top-navigation__group--level-2 " data-category="jj-brands">
<li data-group="accessories">Scarf</li>
<li data-group="bottoms">Shorts</li>
<li data-group="tops">Polo</li>
<li data-group="tops">T-shirt</li>
<li data-group="bottoms">Jeans</li>
<li data-group="accessories">Sunglasses</li>
</ul>
I have made some JavaScript logic which I use to get all the <li> elements and sort them into a two-dimensional array dependent on the data-group.
This means that the sorting would contain the following list:
"<li data-group='accessories'>Scarf</li>"
"<li data-group='accessories'>Sunglasses</li>"
"<li data-group='bottoms'>Shorts</li>"
"<li data-group='bottoms'>Jeans</li>"
"<li data-group='tops'>Polo</li>"
"<li data-group='tops'>T-shirt</li>"
Now what I would like to do is to replace the already existing <li> elements inside the <ul> with the new sorted list. And this is where I am stuck.
CodePen Example Here
Replace the uls innerHTML using a joined Array created from the li-elements that is sorted on its elements using the data-group attribute, mapped to to strings from the elements outerHTML.
const ul = document.querySelector("ul");
ul.innerHTML = Array.from(ul.querySelectorAll("li"))
.sort( (a, b) => a.dataset.group.localeCompare(b.dataset.group) )
.map(v => v.outerHTML)
.join("");
<ul class="category-top-navigation__group category-top-navigation__group--level-2 " data-category="jj-brands">
<li data-group="accessories">Scarf</li>
<li data-group="bottoms">Shorts</li>
<li data-group="tops">Polo</li>
<li data-group="tops">T-shirt</li>
<li data-group="bottoms">Jeans</li>
<li data-group="accessories">Sunglasses</li>
</ul>
Working CodePen.
You need to append the li's to your ul element after clearing it, like :
var ul = document.querySelector('ul[data-category]');
//Clear your ul
ul.innerHTML = "";
for (i = 0; i < uniqueCategoryGroupNames.length; i++) {
sortedCategoryItems[i] = [ ];
for (j = 0; j < categoryItems.length; j++ ){
if (uniqueCategoryGroupNames[i] === categoryItems[j].getAttribute('data-group')) {
sortedCategoryItems[i][j] = categoryItems[j];
//Append your li's
ul.appendChild( sortedCategoryItems[i][j] );
}
}
}
}
So below I have the HTML code that is part of my draggable/droppable:
Drag from here:
<ul id="sortable1" class="dropBox connectedSortable">
<li class="ui-state-default">Word 1</li>
<li class="ui-state-default">Word 2</li>
<li class="ui-state-default">Word 3</li>
<li class="ui-state-default">Word 4</li>
</ul>
Drop a word onto each iamge: <br>
<ul class = "row">
<li id="drop1" class="dropZones images"><img src="image1.jpg"></li>
<li id="drop2" class="dropZones images"><img src="image2.jpg"></li>
</ul>
<ul class = "row">
<li id="drop_zone3" class="dropZones images"><img src="image3.jpg"></li>
<li id="drop_zone4" class="dropZones images"><img src="image4.jpg"></li>
</ul>
</div>
I am trying to create this using document.createElement() etc. But am having difficulty doing this efficiently.
The purpose of the rows (in above code) are for formatting (using bootstrap4), since there are 6 pictures total in my array, and I want 2 rows of 3.
Below, I have an array that holds all the images' src's. This array is passed into the function. I'm able to create one row/ul, with all of the li's/images inside. But I want to be able to create what I have above, with as little hard coding as possible and as efficiently as possible.
I have:
function createType3(arr)
{
var count = 0;
var length = arr.length;
var list = document.createElement("ul");
list.classList.add("row");
for (var i = 0; i < length; i++)
{
//create list and associated ids and classes
var listPiece=document.createElement("li");
listPiece.classList.add("dropZones","images","list-inline");
listPiece.setAttribute("id","dropZone");
//create image tags and append
var imageSRC = document.createElement("IMG");
imageSRC.setAttribute("src",arr[i]);
listPiece.appendChild(imageSRC);
list.appendChild(listPiece);
}
return list;
}
I'm having trouble doing this, thank you.
You could create a document fragment and add list elements to it. Create a new list whenever i % 3 is zero. Make that 3 an argument so that you can use the function also for lists of any other number of entries:
function createType(arr, groupSize = 3) {
var lists = document.createDocumentFragment(),
list;
for (var i = 0; i < arr.length; i++) {
if (i % groupSize === 0) {
list = document.createElement("ul");
list.classList.add("row");
lists.appendChild(list);
}
//create list and associated ids and classes
var listPiece = document.createElement("li");
listPiece.classList.add("dropZones","images","list-inline");
listPiece.setAttribute("id","dropZone");
//create image tags and append
var imageSRC = document.createElement("IMG");
imageSRC.setAttribute("src", arr[i]);
listPiece.appendChild(imageSRC);
list.appendChild(listPiece);
}
return lists;
}
You could call it like this:
var frag = createType(["img1.png","img2.png","img3.png","img4.png"], 3);
document.body.appendChild(frag); // add it somewhere in your HTML page
The following code works, but I think there's room for improvement. The index check is there because after the first element is removed the next element looks like it has an index of -1, but is actually the previously removed element. Then it iterates again and finds the clicked element and removes it. BUT since the index is -1 on the first go around the wrong group gets deleted.
How do I keep the zombie elements from being iterated on more efficiently? This is in a backbone view with an in page confirmation.Thanks.
EDIT: To add HTML
Group section always has a default group that shouldn't be deleted.
<div class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>All Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div><!--/expandable__content-->
Tag section to remove groups
<div class="js-favorite-doctor-manage-add-remove">
<div class="grid-construct">
<div class="expandable" data-controller="expandable">
<ul class="tag-list js-group-list" tabindex="-1">
<li class="tag tag--teal" >
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li class="tag tag--teal" >
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
</ul>
</div>
removeGroup: function( evt ) {
var deleteGroup = function() {
if ( $(evt.currentTarget).closest('.tag').hasClass('is-active')){
var clickedTag = $(evt.currentTarget).closest('.tag');
var groupList = this.$el.find('.js-group-list');
var groupTags = groupList.find('.tag');
var index = groupTags.index(clickedTag);
var groupSections = $('.js-favorite-group');
// add one to account for "All" section which is never removed
var groupToRemove = groupSections.eq(index + 1);
console.log(groupToRemove);
var removedGroupName = this.getGroupNameForSection(groupToRemove);
var allDoctors = groupSections.eq(0);
var allDoctorsContainer = allDoctors.find('.js-favorite-doctor-row-container');
if ( index > -1 ){
groupToRemove.find('.js-favorite-doctor-row').appendTo(allDoctorsContainer);
clickedTag.remove();
groupToRemove.remove();
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
};
this.showAlert(evt, deleteGroup);
},
showAlert: function (evt, callback) {
that = this;
var clickedTag = '';
clickedTag = $(evt.currentTarget).closest('.tag');
clickedTag.addClass('is-active').attr('data-delete','true');
$('.delete-acct-message').show().focus();
$('.js-remove-yes').on('click', function(evt){
evt.preventDefault();
callback.apply(that);
});
$('.js-remove-no').on('click', function(evt){
evt.preventDefault();
this.hideConfirm(evt);
});
},
I would suggest that you should use custom attributes in your html, this will simplify your javascript logic and make it more effective and efficient.
I have modified your html and javascript to add the support for custom attribute data-doc-group. Have a look at your group sections div here
<div data-doc-group="lauren" class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>Lauren's Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div>
Here are the tags with custom attributes
<li data-doc-group="lauren" class="tag tag--teal">
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li data-doc-group="timmy" class="tag tag--teal">
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
Here is the javascript to handle this, (this may be a bit buggy, but will give you a general idea)
removeGroup: function(evt) {
this.showAlert(evt, function() {
var $clickedTag = $(evt.currentTarget).closest('.tag'),
dataGroupName,
$groupToRemove,
removedGroupName,
$allDoctors = $('.js-favorite-group').eq(0),
$allDoctorsContainer = $allDoctors.find('.js-favorite-doctor-row-container');
if ($clickedTag.hasClass('is-active')){
dataGroupName = $clickedTag.data('doc-group');
$groupToRemove = $allDoctors.siblings('[data-doc-group="' + docGroupName + '"]');
if ($groupToRemove.length > 0){
$groupToRemove.find('.js-favorite-doctor-row').appendTo($allDoctorsContainer);
$clickedTag.remove();
$groupToRemove.remove();
removedGroupName = this.getGroupNameForSection($groupToRemove);
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
});
}
I'm using this category filter that exactly matches data attribute product-collection__categoryof the items and the selectors. But I want it a partial or lazy match only. For example, if the selector contains "cats", it will still include items that have "cat","category" etc. I have tried if (categories[i] *= cat) but it's wrong.
Fiddle
HTML
<div class="product-collection">
<ul>
<li class="product-collection__selector
product-collection__selector--active"
data-product-collection__category="">All</li>
<li class="product-collection__selector"
data-product-collection__category="cat1">Category 1</li>
<li class="product-collection__selector"
data-product-collection__category="cat2">Category 2</li>
</ul>
<ul>
<li class="product-collection__item"
data-product-collection__category="cat1">Item 1 [cat 1]</li>
<li class="product-collection__item"
data-product-collection__category="cat2">Item 2 [cat 2]</li>
<li class="product-collection__item"
data-product-collection__category="cat1 cat2">Item 3 [cat 1, cat 2]</li>
</ul>
</div>
Code
$(function() {
$('.product-collection').each(function() {
var $collection = $(this);
var $selectors = $collection.find('.product-collection__selector,.filterselect__selector');
var $items = $collection.find('.product-collection__item');
$selectors.click(function() {
var $selector = $(this);
var cat = $selector.data('product-collection__category');
$selectors.change(function() {
var $selector = $(this);
var cat = $selector.find('option:selected').data('product-collection__category');
$selectors.removeClass('filterselect__selector--active');
$selector.addClass('filterselect__selector--active'); });
if (cat) {
var containsCategory = function(data) {
var categories = data.split(/\s+/);
for (var i = 0; i < categories.length; i++)
if (categories[i] == cat)
return true;
return false;
};
}
else {
$items.fadeIn();
}
});
});
});
I simplified the fiddle so that you could see the selector and test in action - http://jsfiddle.net/cYFLe/64/
$('li').each(function() {
// test the data attribute for partial
if( $(this).is('[data-product-collection__category*="cat"]') ) {
console.log( $(this).text() );
}
});
http://api.jquery.com/is/ is the difference here, it allows the test of the category for truthiness.
I am allowing a user to select hours for each day of the week through the use of lists. Visually the lists are just boxes with 1am, 2am, 3am, etc inside them and when clicked are highlighted to show they are 'selected'. To do this I add a class of 'hour-selected' to the li item.
I also need to keep track of what hours the user has clicked for each day (those with class 'hour-selected'. I know this logic works as I have used this code before, but only for one day. In this example, I need to show all days on the same page.
With a few changes to my original code I came up with this, but I feel there must be some optimization I could do to prevent the repeating js used to push selected hours in for each day, but I am out of ideas and thoughts to do this...
html : (this would repeat for each day of the week with id different for day)
<ol id="hour-select-mon" class="clearfix">
<li class="">12am</li>
<li class="">1am</li>
<li class="">2am</li>
<li class="">3am</li>
<li class="">4am</li>
<li class="">5am</li>
<li class="">6am</li>
<li class="">7am</li>
<li class="">8am</li>
<li class="">9am</li>
<li class="">10am</li>
<li class="">11am</li>
<li class="">12pm</li>
<li class="">1pm</li>
<li class="">2pm</li>
<li class="">3pm</li>
<li class="">4pm</li>
<li class="">5pm</li>
<li class="">6pm</li>
<li class="">7pm</li>
<li class="">8pm</li>
<li class="">9pm</li>
<li class="">10pm</li>
<li class="">11pm</li>
</ol>
JS :
$('#hour-select-mon li, #hour-select-tue li, #hour-select-wed li, #hour-select-thu li, #hour-select-fri li, #hour-select-sat li, #hour-select-sun li').on('click', function() {
$(this).toggleClass('hour-selected');
});
// get selected hours in array
var monHours = var tueHours = var wedHours = var thuHours = var friHours = var satHours = var sunHours = [];
$('#hour-select-mon .hour-selected').each(function(k,v) {
monHours.push($(v).text());
});
$('#hour-select-tue .hour-selected').each(function(k,v) {
tueHours.push($(v).text());
});
$('#hour-select-wed .hour-selected').each(function(k,v) {
wedHours.push($(v).text());
});
$('#hour-select-thu .hour-selected').each(function(k,v) {
thuHours.push($(v).text());
});
$('#hour-select-fri .hour-selected').each(function(k,v) {
friHours.push($(v).text());
});
$('#hour-select-sat .hour-selected').each(function(k,v) {
satHours.push($(v).text());
});
$('#hour-select-sun .hour-selected').each(function(k,v) {
sunHours.push($(v).text());
});
Or if you want to collect all selected items at once you may use a code similar to this:
var selected = {};
$('.hour-selected').each(function(k,v) {
var day = $(v).parent().attr('id').substr(-3, 3);
selected[day] = selected[day] || [];
selected[day].push($(v).text());
});
Try this
var selectedHours = {};
$('ol > li').on('click', function() {
var li = $(this);
li.toggleClass('hour-selected');
var parentId = $(this).parent().attr("id");
selectedHours[parentId] ? selectedHours[parentId].push(li.text()) : selectedHours[parentId] = [li.text()]
});
selectedHours will have the following structure
{
"hour-select-mon" : ["4pm", "6pm"],
"hour-select-tue" : ["9am"],
...
}