I have some code that builds elements of a list on a sidebar. If a button is clicked I want to clear the list and repopulate it with new results. Right now the information just adds to the list. I would like to clear all of the items in the list so I can readd them.
function buildLocationList(data) {
for (i = 0; i < data.locations.length; i++) {
var currentFeature = data.locations[i];
var prop = currentFeature.locations;
var listings = document.getElementById('listings');
var listing = listings.appendChild(document.createElement('div'));
listing.className = 'item';
listing.id = "listing-" + i;
var link = listing.appendChild(document.createElement('a'));
link.href = '#';
link.className = 'title';
link.dataPosition = i;
link.innerHTML = '<b>' + currentFeature.company; + '</b>'
var address = listing.appendChild(document.createElement('div'));
address.innerHTML = currentFeature.address;
var csz = listing.appendChild(document.createElement('div'));
csz.innerHTML = currentFeature.csz;
/*var hours = listing.appendChild(document.createElement('div'));
hours.innerHTML = currentFeature.hours[0].days + ': ' + currentFeature.hours[0].hours;
hours.style.color = 'gray'; */
link.addEventListener('click', function(e){
// Update the currentFeature to the store associated with the clicked link
var clickedListing = data.locations[this.dataPosition];
// 1. Fly to the point
flyToStore(clickedListing);
// 2. Close all other popups and display popup for clicked store
createPopUp(clickedListing);
// 3. Highlight listing in sidebar (and remove highlight for all other listings)
var activeItem = document.getElementsByClassName('active');
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
this.parentNode.classList.add('active');
});
}
}
For your particular case, add this before you do anything else:
document.getElementById('listings').innerHTML = "";
In my web app, a user can create a classroom and then it will be save to the firebase database. After that, the newly created classroom and along with the old class will be retrieve and store in the Classroom: as shown in the image
All the classroom on the list can be click and should show the details of it like: Classroom ID, Teacher, Students. But whenever I do that, an error would appear on the console log
This is the code:
var userRef = firebase.database().ref().child('Classes' + '/' + user.uid);
userRef.on('child_added', function(data) {
var roomNames = data.val().TheClass;
var titsNames = data.val().Teacher;
var classD = data.val().ClassID;
var ul = document.createElement('ul');
document.getElementById('myList').appendChild(ul);
var li = document.createElement('li');
ul.appendChild(li);
Object.keys(roomNames).forEach(function(key){
li.innerHTML += '<span
onclick="clickDone(this)">'+roomNames[key]+'</span><ul style="display:none">
<li>Class Id : '+classD[key]+'</li><li>Teacher : '+titsNames[key]+'</li>
</ul>';
});
function clickDone(thisVar){
var is = thisVar.nextSibling.style.display;
if(is == 'none'){
thisVar.nextSibling.style.display = 'initial';
}else{
thisVar.nextSibling.style.display = 'none';
}
}
});
What went wrong? What did I miss? Any help from you would be greatly appreciated.
The function clickDone must be global to be referred during the onclick event. Move the function clickDone outside of all code, after the last line.
AND the "li" must be created inside the loop
TRY THIS
var userRef = firebase.database().ref().child('Classes' + '/' + user.uid);
userRef.on('child_added', function(data) {
var roomNames = data.val().TheClass;
var titsNames = data.val().Teacher;
var classD = data.val().ClassID;
var ul = document.createElement('ul');
document.getElementById('myList').appendChild(ul);
Object.keys(roomNames).forEach(function(key){
var li = document.createElement('li');
ul.appendChild(li);
li.innerHTML += '<span onclick="clickDone(this)">'+roomNames[key]+'</span><ul style="display:none">' +
'<li>Class Id : '+classD[key]+'</li><li>Teacher : '+titsNames[key]+'</li>' +
'</ul>';
});
});
function clickDone(thisVar){
var is = thisVar.nextSibling.style.display;
if(is == 'none'){
thisVar.nextSibling.style.display = 'initial';
}else{
thisVar.nextSibling.style.display = 'none';
}
}
I'm building a Javascript menu builder which allows a user to items to a menu by filling inputs and then submit the data.
The idea is that on the first click the whole menu is placed in the DOM including the first list item. Subsequent clicks will only put an <li> element in the DOM.
Here is my code. The functions you should look at are createForm and createMenu:
Codepen example
// track the button clicks globally
var btnClicks = 0;
// create a function the append the form to the DOM on page load.
window.onload = createForm;
function createForm() {
// initialize out form container
var f = formContainer();
// declare the variables we will use in one place
var itemName, id, className, target, href, btn, text, h2;
// create an array that will hold the values we wish to pass to our links
// in the menu
var listItem = [itemName, id, className, target, href];
// Create the labels so each input can have a unique label
var labels = ['Name', 'ID', 'Class', 'Target', 'Link'];
// Helper text
h2 = document.createElement('h2');
text = 'Fill in the fields below to create a new menu item';
h2.innerText = text;
f.appendChild(h2);
// loop through the list items
listItem.forEach(function(element, index){
// for each element, call the createDOMNode function and pass in required data
element = createDOMNode(labels[index], 'input', 'text', 'input_' + index);
// append each element to the form container
f.appendChild(element);
});
// create the button and give it an onclick handler
btn = document.createElement('button');
btn.innerText = 'Create Menu Item';
f.appendChild(btn);
btn.onclick = getUserInput;
}
// get what the user inputted into the fields
function getUserInput() {
// initialize some variables and an array
var itemName, id, className, target, href;
item = [];
// access the values from the input fields
values = [
itemName = document.getElementById('input_0').value,
id = document.getElementById('input_1').value,
className = document.getElementById('input_2').value,
target = document.getElementById('input_3').value,
href = document.getElementById('input_4').value,
];
// loop through each value
values.forEach(function(element, index){
// and push it into the item[] array
if(element !== '') {
item.push(element);
}
});
// make sure required items are filled out
if(values[0] === '' && values[4] === '') {
alert('Name and Link are both required');
} else if(values[0] === '') {
alert('Name is required');
} else if(values[4] === '') {
alert('Link is required');
}
// if the array is not empty, then create the menu
if(item.length !== 0) {
createMenu(item);
}
// increase the button counter
btnClicks += 1;
}
// function to create a new menu
function createMenu(item) {
// create elements needed for menu
var nav = document.createElement('nav');
var ul = document.createElement('ul');
var li = document.createElement('li');
var a = document.createElement('a');
var f = document.getElementById('formContainer');
// trying to create the nav only on the first click
if(btnClicks < 1) {
nav.setAttribute('class', 'nav');
document.body.insertBefore(nav, f);
nav.appendChild(ul);
}
var arrayLength = item.length;
// loop through items and set their attributes
for(var i = 0; i < arrayLength; i++) {
li.appendChild(a);
a.innerText = item[0];
a.setAttribute('id', item[1]);
a.setAttribute('class', item[2]);
a.setAttribute('target', item[3]);
a.setAttribute('href', item[4]);
}
// and append them to the ul
ul.appendChild(li);
}
function formContainer() {
// create the element and set where it is displayed in the DOM
var formContainer = document.createElement('div');
formContainer.setAttribute('id', 'formContainer');
var scriptTag = document.getElementsByTagName('script')[0];
document.body.insertBefore(formContainer, scriptTag);
// style the element
formContainer.style.width = '360px';
formContainer.style.margin = '0 auto';
formContainer.style.border = '1px solid #ddd';
formContainer.style.padding = '15px';
return formContainer;
}
function createDOMNode(label, element, type, id) {
var l = document.createElement('label');
l.innerText = label;
// create the node and set it's type attribute
var node = document.createElement(element);
node.setAttribute('type', type);
node.setAttribute('id', id);
// style the node
node.style.padding = '8px 4px';
node.style.width = '100%';
node.style.marginBottom = '10px';
node.style.boxSizing = 'border-box';
l.appendChild(node);
return l;
}
You are creating a new <nav> and <ul> every time you click the button and appending the new <li> item to it. You only add the first <nav> created to the document so all others are being created and never added. Move your variables for creating your <nav> and <ul> outside function createMenu(item)
<script>
// track the button clicks globally
var btnClicks = 0;
// create a function the append the form to the DOM on page load.
window.onload = createForm;
function createForm() {
// initialize out form container
var f = formContainer();
// declare the variables we will use in one place
var itemName, id, className, target, href, btn, text, h2;
// create an array that will hold the values we wish to pass to our links
// in the menu
var listItem = [itemName, id, className, target, href];
// Create the labels so each input can have a unique label
var labels = ['Name', 'ID', 'Class', 'Target', 'Link'];
// Helper text
h2 = document.createElement('h2');
text = 'Fill in the fields below to create a new menu item';
h2.innerText = text;
f.appendChild(h2);
// loop through the list items
listItem.forEach(function(element, index){
// for each element, call the createDOMNode function and pass in required data
element = createDOMNode(labels[index], 'input', 'text', 'input_' + index);
// append each element to the form container
f.appendChild(element);
});
// create the button and give it an onclick handler
btn = document.createElement('button');
btn.innerText = 'Create Menu Item';
f.appendChild(btn);
btn.onclick = getUserInput;
}
// get what the user inputted into the fields
function getUserInput() {
// initialize some variables and an array
var itemName, id, className, target, href;
item = [];
// access the values from the input fields
values = [
itemName = document.getElementById('input_0').value,
id = document.getElementById('input_1').value,
className = document.getElementById('input_2').value,
target = document.getElementById('input_3').value,
href = document.getElementById('input_4').value,
];
// loop through each value
values.forEach(function(element, index){
// and push it into the item[] array
if(element !== '') {
item.push(element);
}
});
// make sure required items are filled out
if(values[0] === '' && values[4] === '') {
alert('Name and Link are both required');
} else if(values[0] === '') {
alert('Name is required');
} else if(values[4] === '') {
alert('Link is required');
}
// if the array is not empty, then create the menu
if(item.length !== 0) {
createMenu(item);
}
// increase the button counter
btnClicks += 1;
}
var nav = document.createElement('nav');
var ul = document.createElement('ul');
// function to create a new menu
function createMenu(item) {
// create elements needed for menu
var li = document.createElement('li');
var a = document.createElement('a');
var f = document.getElementById('formContainer');
// trying to create the nav only on the first click
if(btnClicks < 1) {
nav.setAttribute('class', 'nav');
document.body.insertBefore(nav, f);
nav.appendChild(ul);
}
var arrayLength = item.length;
// loop through items and set their attributes
for(var i = 0; i < arrayLength; i++) {
li.appendChild(a);
a.innerText = item[0];
a.setAttribute('id', item[1]);
a.setAttribute('class', item[2]);
a.setAttribute('target', item[3]);
a.setAttribute('href', item[4]);
}
// and append them to the ul
ul.appendChild(li);
}
function formContainer() {
// create the element and set where it is displayed in the DOM
var formContainer = document.createElement('div');
formContainer.setAttribute('id', 'formContainer');
var scriptTag = document.getElementsByTagName('script')[0];
// document.body.insertBefore(formContainer, scriptTag);
document.body.appendChild(formContainer);
// style the element
formContainer.style.width = '360px';
formContainer.style.margin = '0 auto';
formContainer.style.border = '1px solid #ddd';
formContainer.style.padding = '15px';
return formContainer;
}
function createDOMNode(label, element, type, id) {
var l = document.createElement('label');
l.innerText = label;
// create the node and set it's type attribute
var node = document.createElement(element);
node.setAttribute('type', type);
node.setAttribute('id', id);
// style the node
node.style.padding = '8px 4px';
node.style.width = '100%';
node.style.marginBottom = '10px';
node.style.boxSizing = 'border-box';
l.appendChild(node);
return l;
}
</script>
In the following code, I have some divs with <a> tags, each containing a href of /User.aspx?ID=[some ID]. I wish to click the Delete <a> tag under the parent of the parent of the parent of any divs with duplicate ID's in the href.
Here is my code:
var z = 0;
var info = {};
$("a:contains('Delete')").each(function() {
z++;
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[z] = id;
console.log(info[z]);
});
var uniqueIds = {};
$.each(info, function(i, el){
if($.inArray(el, uniqueIds) === -1) { uniqueIds.push(el) }
else { $("html").find("span.UserLink a[href='/User.aspx?ID='"+info[i]+"']").parent().parent().parent().find("a:contains('Delete')").click() }
});
Use arrays, not objects
Maybe just a typo, I think you wanted to use arrays for info and uniqueIds
var info = [];
var uniqueIds = [];
jQuery.each already provides an index
You don't need z
$("a:contains('Delete')").each(function(index) {
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[index] = id;
console.log(info[z]);
});
Use meaningful names
x and info aren't very good names, you could try (for example) userLinkHref and foundIds
You could store the delete button in the first loop and use it in the second loop
var foundDeleteLinks = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
foundDeleteLinks.push({id:id,$deleteLink:$deleteLink});
console.log(id);
});
var uniqueIds = [];
$.each(foundDeleteLinks, function(i, deleteLink){
var id = deleteLink.id;
if($.inArray(id, uniqueIds) === -1) { uniqueIds.push(id) }
else {
deleteLink.$deleteLink.click();
}
});
You may be able to do it in one loop
var foundIds = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
if($.inArray(id, foundIds) === -1) { foundIds.push(id) }
else { $deleteLink.click(); }
});
I hope that helps enough to find your problems.
i got an anchor in the DOM and the following code replaces it with a fancy button. This works well but if i want more buttons it crashes. Can I do it without a for-loop?
$(document).ready(buttonize);
function buttonize(){
//alert(buttonAmount);
//Lookup for the classes
var button = $('a.makeabutton');
var buttonContent = button.text();
var buttonStyle = button.attr('class');
var link = button.attr('href');
var linkTarget = button.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
if (searchButtonStyle != -1) {
//When class 'makeabutton' is found in string, build the new classname
newButtonStyle = buttonStyle.replace(toSearchFor, toReplaceWith);
button.replaceWith('<span class="'+newButtonStyle
+'"><span class="left"></span><span class="body">'
+buttonContent+'</span><span class="right"></span></span>');
$('.buttonize').click(function(e){
if (linkTarget == '_blank') {
window.open(link);
}
else window.location = link;
});
}
}
Use the each method because you are fetching a collection of elements (even if its just one)
var button = $('a.makeabutton');
button.each(function () {
var btn = $(this);
var buttonContent = btn.text();
var buttonStyle = btn.attr('class');
var link = btn.attr('href');
var linkTarget = btn.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
...
};
the each method loops through all the elements that were retrieved, and you can use the this keyword to refer to the current element in the loop
var button = $('a.makeabutton');
This code returns a jQuery object which contains all the matching anchors. You need to loop through them using .each:
$(document).ready(buttonize);
function buttonize() {
//alert(buttonAmount);
//Lookup for the classes
var $buttons = $('a.makeabutton');
$buttons.each(function() {
var button = $(this);
var buttonContent = button.text();
var buttonStyle = button.attr('class');
var link = button.attr('href');
var linkTarget = button.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
if (searchButtonStyle != -1) {
newButtonStyle = buttonStyle.replace(toSearchFor, toReplaceWith);
button.replaceWith('<span class="'
+ newButtonStyle
+ '"><span class="left"></span><span class="body">'
+ buttonContent
+ '</span><span class="right"></span></span>');
$('.buttonize').click(function(e) {
if (linkTarget == '_blank') {
window.open(link);
} else window.location = link;
}); // end click
} // end if
}); // end each
}