I'm having trouble inserting child elements to a list in HTML using jQuery. I'm a beginner when it comes to anything JavaScript related so my code is probably very clunky and unoptimized. Even if you can't help me with the original issue I always appriciate pointers to improve my code and learn.
I have a function that runs every second (it's called from my CefSharp application, not sure if it's relevant). This is my function:
function updateOrderQueue(index, reference, pickedup, deliveredto, robotId, statuscode, statustext) {
var list = $('#orderQueueList');
var count = $("#orderQueueList li").length;
if (!document.getElementById('order' + reference) && statuscode < 4 && count < 10) {
var data = $(`<li class="list-group-item d-flex justify-content-between align-items-center border border-dark" id="order` + reference + `">
<h3 class="mt-1" id="text`+ reference + `">
[`+index+`] `+ pickedup + ` ➔ ` + deliveredto + ` (` + statustext + `)
</h3>
<small class="badge badge-dark rounded" style="font-size:24px;" id="robotQueue`+ reference + `">` + robotId + `</small>
</li>`);
list.append(data).hide().fadeIn(500);
} else {
var locations = jQuery("#text" + jq(reference));
var assignedrobot = jQuery("#robotQueue" + jq(reference));
locations.text('['+index+'] '+ pickedup + ' ➔ ' + deliveredto + ' (' + statustext + ')');
assignedrobot.text(robotId);
}
var elems = $('#orderQueueList li').detach().sort(function (a, b) {
if (isNaN($(a).text()) || isNaN($(b).text())) {
return $(a).text() > $(b).text() ? 1 : -1;
}
return $(a).text() - $(b).text();
});
list.append(elems);
}
And this is the HMTL code
<div id="orderQueue" class="">
<ul class="list-group" id="orderQueueList">
</ul>
</div>
I'm going to try to quickly explain what I intend on it doing and what it actually does.
So basically, this code is supposed to add, sort and add relevant classes to items in a list. To be added, the item needs to pass a set of requirements (not already added, statuscode under 4 and not more than 10 items total). I then append the item into the list, which I assumed would put it at the end of the list (compared to prepend, which should do the opposite).
The problem is, whenever a new item is added, it is added to the top of the list. My guess is that is has something to do with the fact that I'm appending it to the ul. I tried doing something like $('#orderQueueList li:last') but I didn't get it to work. I've also tried to do $('#orderQueueList).children() and $('#orderQueueList).last() before appending without success.
What am I missing? Have I completely misunderstood what append does? Have I written the code wrong?
Appriciate any pointers,
Thank you in advance.
The issue here is not the .append() but the .sort() which comes after.
Sorting text is affected by whitespace, so the solution is to .trim() the text when doing a text comparison:
$("ul").append("<li>new</li>").hide().fadeIn(500);
var elems = $('ul li').detach().sort(function(a, b) {
if (isNaN($(a).text()) || isNaN($(b).text())) {
return $(a).text().trim() > $(b).text().trim() ? 1 : -1;
}
return $(a).text() - $(b).text();
});
$("ul").append(elems);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li> 2</li>
<li>1555</li>
<li>xyz</li>
<li>3</li>
<li>
abc
</li>
</ul>
Related
I need to concatenate all the title value starting from second li elements with Javascript.
The problem is that I want to use it in different pages, so I can't know the exact number of li elements.
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
I use a variable for each element but if one or more element is missing the var is not valid and the concat function doesn't work.
var a = document.querySelector(".breadcrumb li:nth-child(2) > a").getAttribute("title");
var b = document.querySelector(".breadcrumb li:nth-child(3) > a").getAttribute("title");
var c = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var d = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var str = a.concat(b,c,d);
console.log(str)
Is there a way to do that?
Use querySelectorAll() and map():
const res = [...document.querySelectorAll("#breadcrumb li:not(:first-of-type)")].map(el => el.getAttribute("title")).join(" ")
console.log(res)
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
Using a little jquery i achieved this and it should solve your issues.
let list = [];
$('#breadcrumb li').each(function(i){
if(i !== 0) { list.push($(this).attr('title')); }
});
list.toString() //One,Two,Three,Four
The method you tried to use wont scale on a large list
Two minor remarks:
If you want to access the id=breadcrumb, you have to use #breadcrumb instead of .breadcrumb
There is no a tag in your HTML-code, therefore your querySelector won't give you any result
However, let's discuss a solution:
let listElements = document.querySelectorAll("#breadcrumbs li"); // get all list elements
listElements = Array.from(listElements); // convert the NodeList to an Array
listElements = listElements.filter((value, index) => index >= 1); // remove the first element
let titleAttributes = listElements.map(listElement => listElement.getAttribute("title")); // get the title attributes for every list elements. The result is an array of strings containing the title
console.log(titleAttributes.join(", ")); // concatenate the titles by comma
You can write the above statements in a single line:
Array.from(document.querySelectorAll("#breadcrumbs li"))
.filter((value, index) => index >= 1)
.map(listElement => listElement.getAttribute("title"))
.join(", ");
EDIT: I fix my answer, thanks to Barmar
something like that ?
const All_LI = [...document.querySelectorAll('#breadcrumb li')];
let a = ''
for (let i=1; i<All_LI.length; i++) { a += All_LI[i].title }
console.log('a-> ', a )
// .. or :
const b = All_LI.reduce((a,e,i)=>a+=(i>0)?e.title:'', '' )
console.log('b-> ', b )
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
I am trying to get elements that contains a certain string or value based on their data-attribute and the sort them because they are dates. I found a nice JSFiddle example that takes the data attribute value and then converts it into a Epoch date format (fine for now), and then sorts them. BUT it only sorts them on a self invoking function page load the first time. When I wrapped it in a function and tried calling it again, the order switch from ASC to DESC instead of just keeping an ASC or DESC order.
That said, I have a JSFiddle based on the example I found, but now when I try to continually sort the dates and keep an ASC or DESC format, the data value that got convert to Epoch time is getting NaN.
So I tried wrapping it in an IF statement to see IF the data attribute value has a "/" in it but that does not seem to be working right now either. I have a bunch of comments in my Fiddle I have been working on. I am very close, but it is when I try to click the function to sort each time that I want the order to stay the same without erroring out (check the console you will see the NaN).
This is the Fiddle example using the original code, BUT I wrapped it in a function and call it via a click and the order just reverses every time. I only want it to go ASC or DESC, not switch back and forth.
http://jsfiddle.net/r8hbx2e3/1/
//ORIGINALLY BASED ON http://jsfiddle.net/greguarr/2fr0vmhu/
//BUT THIS JUST REORDERS EVERYTHING ASC AND DESC ON EVERY CLICK. JUST WANT IT ONE OR THE OTHER
//HOW DO YOU GET THIS TO STOP REVERSING ORDER.
//ONE I HAVE BEEN WORKING ON IS HERE: http://jsfiddle.net/ndh9Lyuj/5/
function sortItems() {
var container = $(".sort-list");
var items = $(".sort-item");
items.each(function() {
// Convert the string in 'data-event-date' attribute to a more
// standardized date format
var BCDate = $(this).attr("data-event-date").split("/");
var standardDate = BCDate[0] + " " + BCDate[1] + " " + BCDate[2];
standardDate = new Date(standardDate).getTime();
$(this).attr("data-event-date", standardDate);
});
items.sort(function(a, b) {
a = parseFloat($(a).attr("data-event-date"));
b = parseFloat($(b).attr("data-event-date"));
return a > b ? -1 : a < b ? 1 : 0;
}).each(function() {
container.prepend(this);
});
};
$('p').on('click', function() {
sortItems();
});
/* This script sorts your list in descending order... to change it to ascending order change the "less than" operator (<) to "greater than" (>) */
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<ul class="sort-list">
<li class="sort-item" data-event-date="01/20/2019">01/20/2019</li>
<li class="sort-item" data-event-date="10/15/2017">10/15/2017</li>
<li class="sort-item" data-event-date="11/14/2018">11/14/2018</li>
<li class="sort-item" data-event-date="02/05/2016">02/05/2016</li>
<li class="sort-item" data-event-date="10/31/2013">10/31/2013</li>
</ul>
<p>
Click me
</p>
This is the more extensive Fiddle I have been working on trying to get multiple options to work but keeping the order from switching. But it doesn't quite work because you will see in the consoles the NaN is coming back each time after the first click through.
http://jsfiddle.net/ndh9Lyuj/5/
//ORIGINALLY BASED ON http://jsfiddle.net/greguarr/2fr0vmhu/
var container = $(".sort-list");
var items = $(".sort-item");
var yourArray = [];
function me() {
items.each(function() {
console.log("up top" + $(this).attr("data-event-date"));
// Convert the string in 'data-event-date' attribute to a more
// standardized date format
if ($(this).attr("data-event-date").indexOf("/")) { //This does not seem to be working to check if data attribute has value with a slash.
var BCDate = $(this).attr("data-event-date").split("/");
var standardDate = BCDate[0] + " " + BCDate[1] + " " + BCDate[2];
standardDate = new Date(standardDate).getTime();
$(this).attr("data-event-date", standardDate);
console.log("with standard date " + $(this).attr("data-event-date"));
}
console.log($(this).attr("data-event-date"));
// yourArray.push($(this).prop('data-event-date').split(' ')[0]);
});
// nowSort(items);
// }
//function nowSort(items){
items.sort(function(a, b) {
a = parseFloat($(a).attr("data-event-date"));
b = parseFloat($(b).attr("data-event-date"));
// if(a < b){
// return -1
//}
// return 0;
// a = $(a).attr("data-event-date");
// b = $(b).attr("data-event-date");
return a > b ? -1 : a < b ? 1 : 0;
// return a > b;
//alert('sort');
})
.each(function() {
container.prepend(this);
});
//alert('sort');
}
$('p').on('click', function() {
me();
});
/* This script sorts your list in descending order... to change it to ascending order change the "less than" operator (<) to "greater than" (>) */
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<ul class="sort-list">
<li class="sort-item" data-event-date="04/20/2019">04/20/2019</li>
<li class="sort-item" data-event-date="03/05/2017">03/05/2017</li>
<li class="sort-item" data-event-date="04/18/2019">04/18/2019</li>
<li class="sort-item" data-event-date="01/20/2016">01/20/2016</li>
<li class="sort-item" data-event-date="10/31/2019">10/31/2019</li>
<li class="sort-item" data-event-date="02/30/2019">02/31/2019</li>
</ul>
<p>
click me
</p>
Any help is appreciated.
Changed the method to pass in asc or desc
Only convert the date once, and convert it to ISO format
Do not override the original data field
When sorting, check if the value is asc or not to determine which way to sort
$('p').on('click', function(){ sortItems('desc'); });
function sortItems (sortDirection) {
var container = $(".sort-list");
var items = $(".sort-item");
items.filter(function(){
return !$(this).data('convertedEventDate');
}).each(function() {
var BCDate = $(this).data("eventDate").split("/");
var standardDate = `${BCDate[2]}-${BCDate[0]}-${BCDate[1]}`;
standardDate = new Date(standardDate).getTime();
$(this).data("convertedEventDate", standardDate);
});
container.append(
items.sort(function(a, b){
var $aTime = $(a).data('convertedEventDate');
var $bTime = $(b).data('convertedEventDate');
if (sortDirection === 'asc') {
return $aTime - $bTime;
} else {
return $bTime - $aTime;
}
})
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<ul class="sort-list">
<li class="sort-item" data-event-date="01/20/2019">01/20/2019</li>
<li class="sort-item" data-event-date="10/15/2017">10/15/2017</li>
<li class="sort-item" data-event-date="11/14/2018">11/14/2018</li>
<li class="sort-item" data-event-date="02/05/2016">02/05/2016</li>
<li class="sort-item" data-event-date="10/31/2013">10/31/2013</li>
</ul>
<p>Click me</p>
I have a link in a angular 6 webapp and when I click it i want to use removeItem ( the item is dateFilter) from the Local Storage. I'm using #ngx-pwa/local-storage
My method in my app.components.ts
clearFilter(){
this.storage.removeItem('dateFilter').subscribe(() => {});
}
My HTML app.component.html
<li *ngIf="user.storeOwner || menu.includes('report_employees')">
<a class="sub-links" (click)="sendToPage('/employees-report', langs.reports + ' > ' + langs.professionals); clearFilter()">
{{langs.professionals || 'Profissionais' }}
</a>
</li>
When i click everything works fine, but it doesn't remove the item dateFilter and doesn't show any errors.
It was a problem of competition with the method clearFilter() and SendToPage and I call the sendToPage inside the storage method like that:
HTML (inside the click)
(click)="clearFilter('/employees-report', langs.reports + ' > ' + langs.professionals)"
Method:
clearFilter(url, desc){
this.storage.removeItem('dateFilter').subscribe(() => {
this.sendToPage(url, desc);
});
}
And i pass the parameters to the method to remove the item before sendToPage
I'm sorry I can't be any more specific - I have no idea where the problem is. I'm a total beginner, and I've added everything I know to add to the coding, but nothing happens when I push the button. I don't know at this point if it's an error in the coding, or a syntax error that makes it not work.
Basically I am trying to get this function "Rip It" to go through the list of Dewey decimal numbers, change some of them, and return the new number and a message saying it's been changed. There is also one labeled "no number" that has to return an error (not necessarily an alert box, a message in the same space is okay.)
I am a total beginner and not particularly good at this stuff, so please be gentle! Many thanks!
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function RipIt()
{
for (var i = l; i <=10 i=i+l)
{
var dewey=document.getElementById(i);
dewey=parseFloat(dewey);
if (dewey >= 100 && 200 >= dewey)
{
document.getElementById('dewey'+ 100)
}
else if (dewey >= 400 && 500 >= dewey)
{
document.getElementById('dewey'+ 200)
}
else if (dewey >= 850 && 900 >= dewey)
{
document.getElementById('dewey'-100)
}
else if (dewey >= 600 && 650 >= dewey)
{
document.getElementById('dewey'+17)
}
}
}
</script>
</head>
<body>
<h4>Records to Change</h4>
<ul id="myList">
<li id ="1">101.33</li>
<li id = "2">600.01</li>
<li id = "3">001.11</li>
<li id = "4">050.02</li>
<li id = "5">199.52</li>
<li id = "6">400.27</li>
<li id = "7">401.73</li>
<li id = "8">404.98</li>
<li id = "9">no number</li>
<li id = "10">850.68</li>
<li id = "11">853.88</li>
<li id = "12">407.8</li>
<li id = "13">878.22</li>
<li id = "14">175.93</li>
<li id = "15">175.9</li>
<li id = "16">176.11</li>
<li id = "17">190.97</li>
<li id = "18">90.01</li>
<li id = "19">191.001</li>
<li id = "20">600.95</li>
<li id = "21">602.81</li>
<li id = "22">604.14</li>
<li id = "23">701.31</li>
<li id = "24">606.44</li>
<li id = "25">141.77</li>
</ul>
<b> </b>
<input type="button" value="Click To Run" onclick="RipIt()">
<!-- <input type="button" value="Click Here" onClick="showAlert();"> -->
</body>
</html>
I see a few issues:
You need to ensure that the id values in the HTML match what you actually feed into getElementById. For instance, you have document.getElementById('dewey'+ 100) which will look for an element with the id value "dewey100", but I don't see any element in your markup with that id value.
You seem to have typed the lower-case letter l where you meant to type the digit 1 (in your for loop).
This code:
var dewey=document.getElementById(i);
dewey=parseFloat(dewey);
...retrieves the element with the id from i (so far so good), but then tries to parse the element as a floating-point number. DOM elements are objects, passing them into parseFloat won't do anything useful. :-) In this case, if you're trying to parse the content of the element, you can get that via the innerHTML property or (on most browers) innerText for just the text. So perhaps:
var dewey=document.getElementById(i);
dewey=parseFloat(dewey.innerHTML);
The line
document.getElementById('dewey'+ 100)
...by itself is a "do nothing" line: It looks up the element, but then doesn't do anything with it. I'd suggest a change, but I have no idea what you're trying to do with that element. :-)
You may not be aware of it (being new), but your browser almost certainly has quite a powerful tool in it called a "debugger". Look on the menus, but in most browsers you can access the "Dev Tools" using the F12 key. Then you go to the "Source" or "Code" tab in the resulting window, which will show you your code. If you click to the left of a line, in most debuggers that sets a breakpoint which will stop the code in its tracks at that point so you can see what's going on. It's worth spending some time learning to use that tool so you can actually watch your code run.
Editing my old answer...
For your HTML, I removed the id's in the list items since you can find a better way to iterate through them. This way you don't have to add a new id when you want to add an li. See below:
<h4>Records to Change</h4>
<ul id="myList">
<li>101.33</li>
<li>600.01</li>
<li>001.11</li>
<li>050.02</li>
<li>199.52</li>
<li>400.27</li>
<li>401.73</li>
<li>404.98</li>
<li>no number</li>
<li>850.68</li>
<li>853.88</li>
<li>407.8</li>
<li>878.22</li>
<li>175.93</li>
<li>175.9</li>
<li>176.11</li>
<li>190.97</li>
<li>90.01</li>
<li>191.001</li>
<li>600.95</li>
<li>602.81</li>
<li>604.14</li>
<li>701.31</li>
<li>606.44</li>
<li>141.77</li>
</ul>
<input type="button" value="Click To Run" onclick="RipIt()">
For your javascript, I found the number of li's and stored in children. Then found the length of this array and set to 'length'. Then I pulled the innerHTML from each child item in the children array and parseFloat'ed it. Then I ran your conditional and created a new value based on the child's value.
Finally that value is stored in the children li item in question.
the JavaScript:
function RipIt() {
var children = document.getElementById("myList").getElementsByTagName("li"),
length = children.length;
for (var i = 0; i < length; i++) {
var child = children[i].innerHTML,
newValue;
child = parseFloat(child);
if (child >= 100 && 200 >= child) {
newValue = child + 100;
} else if (child >= 400 && 500 >= child) {
newValue = child + 200;
} else if (child >= 850 && 900 >= child) {
newValue = child - 100;
} else if (child >= 600 && 650 >= child) {
newValue = child + 17;
}
children[i].innerHTML = newValue;
}
}
You will probably need to work on your conditionals (if/else) to get exactly what you want. You didn't really specify what each condition needed to do in your answer so I just used your original code.
I have an unordered list of elements organized in rows. When a user clicks on a row, I want the text in the row to be appended into a separate text field. The issue with my current code is that I if the user clicks multiple boxes, all of the associated text with each of those boxes will be appended into the textfield. I would like to append the text from only the last row element that the user clicked.
Here is my javascript:
function clickEvents() {
// Day List Selector
$('#DC_id_1').click(function() {
$('#whenTextField').attr('value', 'Today');
});
$('#DC_id_3').click(function() {
$('#whenTextField').attr('value', 'Tomorrow');
});
$('#DC_id_5').click(function() {
$('#whenTextField').attr('value', 'Later');
});
// Time List Selector
$('#DC_id_37').click(function() {
var day = $('#whenTextField').attr('value');
$('#whenTextField').attr('value', day + ', Right Now');
});
$('#DC_id_39').click(function() {
var day = $('#whenTextField').attr('value');
$('#whenTextField').attr('value', day + ', Morning');
});
$('#DC_id_41').click(function() {
var day = $('#whenTextField').attr('value');
$('#whenTextField').attr('value', day + ', Midday');
});
$('#DC_id_43').click(function() {
var day = $('#whenTextField').attr('value');
$('#whenTextField').attr('value', day + ', Afternoon');
});
$('#DC_id_45').click(function() {
var day = $('#whenTextField').attr('value');
$('#whenTextField').attr('value', day + ', Evening');
});
}
Basically, I think I want to use an "if" statement to control the clicking in the Time List Selector elements list.
example:
if (DC_id_37 is clicked) {
append('text');
}
else if (DC_id_39 is clicked) {
append('some other text');
Here is the associated HTML:
<ul id="dayList">
<li id="DC_id_1">
Today
</li>
<li id="DC_id_3">
Tomorrow
</li>
<li id="DC_id_5">
Later
</li>
</ul>
<ul id="timeList">
<li id="DC_id_37">
Right Now
</li>
<li id="DC_id_39">
Morning
</li>
<li id="DC_id_41">
Midday
</li>
<li id="DC_id_43">
Afternoon
</li>
<li id="DC_id_45">
Evening
</li>
</ul>
<textField id="whenTextField">
*Note I just created this HTML by hand, as I'm building the web app in Dashcode, and its putting out some very ugly HTML
Actual HTML created by Dashcode:
<ul id="timeList">
<li>
<div id="foo"></div>
<div id="DC_id_37">Right Now</div>
<div></div>
</li>
<li>
<div id="foo2"></div>
<div id="DC_id_39"></div>
<div></div>
</li>
</ul>
Instead of simply appending the new value, you need to replace the old value. I would do it this way:
// Based on the HTML you posted, we only need two click functions.
$("#dayList > li > div:eq(1)").click(function() {
var newDate = $(this).text();
var currentValues = $("#whenTextField").attr("value").split(", ");
// Initialize this as an empty string in case the user has not yet selected
// a time.
var time = "";
// Get the previous time, if one has already been appended.
if (currentValues.length == 2) {
time = ", " + currentValues[1];
}
// Update the value of the text field.
$("#whenTextField").attr("value", newDate + time);
});
$("#timeList > li > div:eq(1)").click(function() {
// Get the current date value.
var date= $("#whenTextField").attr("value").split(", ")[0];
// Get the text from the 'li' element that raised the event.
var time = $(this).text();
// Update the value of the text field, keeping the previously selected date.
$("#whenTextField").attr("value", date + ", " + time);
});
This approach also saves the selected time if a user later changes the selected date.
Update:
I updated my example to match the second nested <div /> element under each <li /> using the :eq(n) selector (the selector is zero-based). This solution assumes that the target <div /> elements will always be the second one, based on your comment.
You have a few other options, too:
You could use the :parent selector to affect only <divs /> that contain text (or any child elements).
$("#timeList > li > div:parent").click(function() { });
Or, you could use the Attribute Starts With Selector to affect only <div /> elements with IDs that start with "DC_id_".
$("#timeList > li > div[id^='DC_id_']").click(function() { });
I'm not sure which solution performs the best. Personally, I would recommend going with the third option (the Attribute Starts With selector) since generated IDs are usually more predictable than an element's index or contents.
I believe this is what you're looking for:
$('#DC_id_37').click(function() {
var day = $('#whenTextField').attr('value').split(",")[0];
$('#whenTextField').attr('value', day + ', Right Now');
});
The .split(",")[0] will grab the first part of what's in the text box.
You could simplify your code with something like this:
$('#dayList li').click(function() {
$('#whenTextField').attr('value',$(this).text());
});
$("#timeList li").click(function() {
var day = $('#whenTextField').attr('value').split(",")[0];
$('#whenTextField').attr('value', day + ', '+$(this).text());
});
Roughly
html: (adjust to taste, prolly have to do some css)
<div class="appointment">
<div class="options">
<ul class="day">
<li>Today</li>
<li>Tomorrow</li>
<li>Later</li>
</ul>
<ul class="time">
<li>Right Now</li>
<li>Morning</li>
<li>Midday</li>
<li>Afternoon</li>
<li>Evening</li>
</ul>
</div>
<input class="when" type="text" />
</div>
jquery
$('.appointment').each(function(){
var self = $(this)
, whenField = self.find('.when')
, day = 'choose a day'
, time = 'choose a time';
self.delegate('.options li', 'click', function(e){
var li = $(this), ul = li.closest('ul');
if(ul.hasClass('day')) {
day = li.text();
} else if(ul.hasClass('time')) {
time = li.text();
}
whenField.val(day + ' - ' + time);
});
});
Reusable on a page, so you can have more appointment boxes on a page.
I personally avoid using element ID's like the plague, better abstract it and shorten code substantially if you generalize.