sorting <li> elements alphabetically - javascript

How can I sort an unordered list alphabetically while retaining outer html? My current setup sorts the list alphabetically, however it only rearranges the inner html of the list elements rather than the entire element, which is a problem because within the tag i have event based script calls that are specific to each element. The list elements themselves are added by script from an xml document.
Here's the html:
var xhttp;
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
onLoad(this);
}
};
xhttp.open("GET", "stocks.xml", true);
xhttp.send();
function onLoad(xml) {
var x, i, txt, xmlDoc;
xmlDoc = xml.responseXML;
txt = "<ul id = stocksymbols>";
var StockList;
x = xmlDoc.getElementsByTagName("Stock");
for (i = 0; i < x.length; i++) {
symbol = x[i].getAttribute('symbol');
txt += "<li onmouseover=\"mouseOver('" + symbol + "')\" onmouseout=\"mouseOut()\">" + symbol + "</li>";
}
document.getElementById("stockList").innerHTML = txt + "</ul>";
sortList("stocksymbols");
}
function sortList(ul) {
if (typeof ul == "string")
ul = document.getElementById(ul);
var lis = ul.getElementsByTagName("LI");
var vals = [];
for (var i = 0, l = lis.length; i < l; i++)
vals.push(lis[i].innerHTML);
vals.sort();
for (var i = 0, l = lis.length; i < l; i++)
lis[i].innerHTML = vals[i];
}
function mouseOver(target) {
stockInfoDiv = document.getElementById("stockInfo");
stockInfoDiv.innerHTML = target;
}
function mouseOut() {
stockInfoDiv.innerHTML = "";
}
h2 {
color: Navy;
}
li {
font-family: monospace;
font-weight: bold;
color: Navy;
}
li:hover {
font-family: monospace;
font-weight: bold;
color: red;
}
<html>
<head>
<title></title>
</head>
<body>
<h2>List of Stocks:</h2>
<div id="stockList">
</div>
<br />
<br />
<br />
<div id="stockInfo">
</div>
</body>
</html>

Instead of lis[i].innerHTML = vals[i];, sort the lis list and do ul.appendChild(lis[i]). This will remove the current li from its position in the DOM and append it to the end of the ul. I'm assuming the only li elements are direct children of the ul.
function sortList(ul) {
var ul = document.getElementById(ul);
Array.from(ul.getElementsByTagName("LI"))
.sort((a, b) => a.textContent.localeCompare(b.textContent))
.forEach(li => ul.appendChild(li));
}
sortList("stocksymbols");
<ul id=stocksymbols>
<li>AAA</li>
<li>ZZZ</li>
<li>MMM</li>
<li>BBB</li>
</ul>

<ul id="mylist">
<li id="list-item3">text 3</li>
<li id="list-item4">text 4</li>
<li id="list-item2">text 2</li>
<li id="list-item1">text 1</li>
</ul>
<script>
var list = document.getElementById('mylist');
var items = list.childNodes;
var itemsArr = [];
for (var i in items) {
if (items[i].nodeType == 1) { // get rid of the whitespace text nodes
itemsArr.push(items[i]);
}
}
itemsArr.sort(function(a, b) {
return a.innerHTML == b.innerHTML
? 0
: (a.innerHTML > b.innerHTML ? 1 : -1);
});
for (i = 0; i < itemsArr.length; ++i) {
list.appendChild(itemsArr[i]);
}
</script>

So lets do it with the XML, build an array, sort the array, and than build the lis.
var symbols = xmlDoc.getElementsByTagName("Stock");
var items = [];
for (var i = 0; i < symbols.length; i++) {
items.push(symbols[i].getAttribute('symbol')); //build array of the symbols
}
var lis = items.sort() //sort the array
.map( function(txt) { //loop over array
return "<li>" + txt + "</li>"; //build the li
}).join(""); //join the indexes as one string
console.log(lis); //the lis in a string.

Related

How to target a specific part of an array in an ordered list

How can I target the last names to be red? In the HTML, I have an ID named elements and the same in CSS to make the last names red. I tried to do various things but I'm finding no success.
var names = ["Jane Doe", "Jose Munez", "John Williams"];
var splitNames = names.sort().reverse();
function anything(str1) {
for (i = 0; i < str1.length; i++) {
str1[i] = str1[i].split(",").join(" ");
return str1;
}
}
console.log(anything(splitNames));
//getLastName : takes argument 'str', splits the string passed in after there's a space and retrieves all characters after the split (that's what the 1 means)
function getLastName(str) {
return str.split(' ')[1];
}
function orderedList(listItem) {
for (var i = 0; i < listItem.length; i++) {
var l = document.createElement('li');
l.innerHTML = listItem[i];
document.getElementById('elements').appendChild(l);
console.log(getLastName(listItem[i]));
var list = "";
}
}
orderedList(splitNames);
I split the names up into 2, first name and last name. Then I used a span element to enclose the last name. The span has a class, so I then applied CSS to the class:
var names = ["Jane Doe", "Jose Munez", "John Williams"];
var splitNames = names.sort().reverse();
function anything(str1) {
for (i = 0; i < str1.length; i++) {
str1[i] = str1[i].split(",").join(" ");
return str1;
}
}
console.log(anything(splitNames));
//getLastName : takes argument 'str', splits the string passed in after there's a space and retrieves all characters after the split (that's what the 1 means)
function getLastName(str) {
return str.split(' ')[1];
}
//New function just for convenience
function getFirstName(str) {
return str.split(' ')[0];
}
function orderedList(listItem) {
for (var i = 0; i < listItem.length; i++) {
var l = document.createElement('li');
//Notice how the span class is used to enclose the last name
l.innerHTML = getFirstName(listItem[i]) + " <span class=" + 'last-name' + ">" + getLastName(listItem[i]) + "</span>";
document.getElementById('elements').appendChild(l);
console.log(getLastName(listItem[i]));
var list = "";
}
}
orderedList(splitNames);
.last-name {
color: red;
}
<ul id="elements">
</ul>
I manipulated the li's innerHTML to include a span with class last-name. Then I applied color: red to all last-names.
Is this what you want ?
<style>
span.red-name {
color : red;
}
</style>
<ul id="elements">
</ul>
<script>
var names = ["Jane Doe", "Jose Munez", "John Williams"];
var splitNames = names.sort().reverse();
function orderedList(listItem) {
for (var i = 0; i < listItem.length; i++) {
var l = document.createElement('li');
var flname = listItem[i].split(' ');
l.innerHTML = flname[0] + ' <span class="red-name">'+flname[1] + '</span>';
document.getElementById('elements').appendChild(l);
}
}
orderedList(splitNames);
</script>

JavaScript access anchor in list

Currently my code iterates through each <li> within a <td> cell and applies a class to the <li>. I've now added <a> tags in between each <li> and am having problems accessing the <a>. I essentially want to add a class to each <a> tag rather than the <li>.
HTML
<td style='padding: 0;' bgcolor='#FAFAFA'>
<ul class='doctorList'>
<li id='1'><a style='text-decoration: none;'>Curly</a></li>
<li id='2'>Larry</li>
<li id='3'>Moe</li>
</ul>
</td>
JavaScript
function mapBookedAppointmentsToCalendar()
{
var bookedAppointmentsArray = <?php echo json_encode($mappingIdArray) ?>;
var table = document.getElementById("tbl_calendar");
for (var i = 0, row; row = table.rows[i]; i++) {
for (var j = 0, col; col = row.cells[j]; j++) {
var li = col.querySelectorAll("li");
for (var k = 0; k < li.length; k++) {
for (var a = 0; a < bookedAppointmentsArray.length; a++)
{
if (li[k].id == bookedAppointmentsArray[a])
{
li[k].className = "colorRed booked";
break;
} else
{
li[k].className = "colorGreen";
}
}
}
}
}
}
Did you try using the query selector to find those <a> ?
var li = col.querySelectorAll("#tbl_calendar li a");
for (var k = 0; k < li.length; k++) {
for (var a = 0; a < bookedAppointmentsArray.length; a++)
{
if (li[k].id == bookedAppointmentsArray[a])
{
li[k].className = "colorRed booked";
break;
} else
{
li[k].className = "colorGreen";
}
}
}
You don't need to use table to access it. Just keep in mind getElementsByClassName method:
u = document.getElementsByClassName('doctorList');
for (i = 0; i < u.length; i++){
l = u[i].getElementsByTagName('li');
for (j = 0; j < l.length; j++){
l[j].className = 'red';
}
}
Checkout this demo
If you don't need ancient browsers support, you can do this:
var ul = document.querySelector('ul.doctorList');
var li = ul.querySelectorAll('li');
// convert the node list to an array
li = [].slice.call(li);
li.forEach(function(element) {
if (element.id === '1') {
var a = element.querySelector('a');
a.className = 'red';
}
});
var ul = document.querySelector('ul.doctorList');
var li = ul.querySelectorAll('li');
// convert the node list to an array
li = [].slice.call(li);
li.forEach(function(element) {
if (element.id === '1') {
var a = element.querySelector('a');
a.className = 'red';
}
});
.red {
color: red;
}
<td style='padding: 0;' bgcolor='#FAFAFA'>
<ul class='doctorList'>
<li id='1'><a style='text-decoration: none;'>Curly</a></li>
<li id='2'>Larry</li>
<li id='3'><a style='text-decoration: none;'>Moe</a></li>
</ul>
</td>

adding a span to all the numbers in the body tag of the website using javascript

I want to add span tag with the specific class to all the numbers in my website using JavaScript. However I got the following:
<script>
var regex = /(\d+)/,
replacement = '<span class="font-arial">$1</span>';
function replaceText(el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
var nodes = temp_div.childNodes;
while (nodes[0]) {
el.parentNode.insertBefore(nodes[0],el);
}
el.parentNode.removeChild(el);
}
} else if (el.nodeType === 1) {
for (var i = 0; i < el.childNodes.length; i++) {
replaceText(el.childNodes[i]);
}
}
}
replaceText(document.body);
</script>
But the problem is as below example:
If the number is: 45 7320272536
It put like this:
<span class="arial">
<span class="arial">45</span>
</span>
<span class="arial">
<span class="arial">7320272536</span>
</span>
I want like this:
<span class="arial">45</span>
<span class="arial">7320272536</span>
It can be simpler. You don't need to loop over child nodes one more time. Instead you can replace all number occurrences at once if you use global match flag g for regexp object:
/(\d+)/g
So after cleaning the code with replaceChild method, your code becomes:
var regex = /(\d+)/g,
replacement = '<span class="font-arial">$1</span>';
function replaceText(el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
el.parentNode.replaceChild(temp_div, el);
}
} else if (el.nodeType === 1) {
for (var i = 0; i < el.childNodes.length; i++) {
replaceText(el.childNodes[i]);
}
}
}
replaceText(document.body);
Demo: http://jsfiddle.net/a09vac2t/
Here is a working sample.
<!doctype html>
<html>
<head>
<title>replace test</title>
<script>
var regex = /(\d+)/;
replacement = '<span style="border:1px solid red">$1</span>';
function replaceText(el) {
//alert('Testing: ' + el.data);
//alert('Node type: ' + el.nodeType);
if (el.nodeType === 3) {
if (regex.test(el.data)) {
//alert('Matches');
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
//alert('Making it ' + temp_div.innerHTML);
var nodes = temp_div.childNodes;
while (nodes[0]) {
//alert(nodes.length + ": " + nodes[0].data);
el.parentNode.insertBefore(nodes[0],el);
}
el.parentNode.removeChild(el);
}
} else if (el.nodeType === 1) {
//alert('Looping children: ' + el.childNodes.length);
var rootChildrenCopy = toArray(el.childNodes).slice(0);
for (var i = 0; i < rootChildrenCopy.length; i++) {
replaceText(rootChildrenCopy[i]);
}
}
}
function toArray(obj) {
var array = [];
// iterate backwards ensuring that length is an UInt32
for (var i = obj.length >>> 0; i--;) {
array[i] = obj[i];
}
return array;
}
</script>
</head>
<body onload="replaceText(document.getElementById('abc'))">
<p>This is a number</p>
<div id='abc'>Here is a 45 num</div>
</body>
</html>
Element.childNodes will change when you insert nodes, so you need to save a copy of it when looping. Array.prototype.slice is good for this.
In addition, you need to change your regex to replace all occurrences instead of just the first with the global flag.

How to sort <ul><li>'s based on class with javascript?

I have a TODO list app with an Unordered list. Within it I have a few list items. The li classes are high,medium,low. I would like li's with the class high to be placed before li's with the class medium and last ones with low.
<ul id="tasks">
<li id="item3" class="priority low"><span></span><span>This is a low priority task</span></li>
<li id="item4" class="priority high"><></span><span>This is a high priority task</span></li>
<li id="item5" class="priority low"><span></span><span>This is another Low</span></li>
<li id="item7" class="priority medium"><span></span><span>And now a Medium</span></li>
</ul>
So the li with id of item4 should be first and then it should be item7 and then the li's with class low after.
Here's a pure JS version of #ŠimeVidas jQuery solution.
var tasks = document.querySelector('#tasks'),
items = document.querySelectorAll('#tasks > li');
for (var i = 0, arr = ['high', 'medium', 'low']; i < arr.length; i++) {
for (var j = 0; j < items.length; j++) {
if (~(" " + items[j].className + " ").indexOf(" " + arr[i] + " "))
tasks.appendChild(items[j]);
}
}
Assuming you can use jQuery, and assuming your list is not very big, and assuming you've only got these three fixed types with no plans on changing this, I'd probably just dump the whole set into memory, clear out the list, then put them back in the list in order. Something like:
jQuery(document).ready(function() {
var i;
var items = jQuery("#tasks li");
var lowItems = [];
var medItems = [];
var highItems = [];
for (i = 0; i < items.length; ++i) {
var jqItem = jQuery(items[i]);
if (jqItem.hasClass("low")) lowItems.push(jqItem);
if (jqItem.hasClass("medium")) medItems.push(jqItem);
if (jqItem.hasClass("high")) highItems.push(jqItem);
}
var tasks = jQuery("#tasks");
tasks.html("");
for (i = 0; i < highItems.length; ++i) {
tasks.append(highItems[i]);
}
for (i = 0; i < medItems.length; ++i) {
tasks.append(medItems[i]);
}
for (i = 0; i < lowItems.length; ++i) {
tasks.append(lowItems[i]);
}
});
Try this:
$(function(){
var sorter = [],
tasks = $('#tasks');
$('li.priority').each(function(){
var $this = $(this),
priority = $this.hasClass('high') ? 3 : ($this.hasClass('medium') ? 2 : 1);
sorter.push({
el : this,
priority : priority
});
}).detach();
sorter.sort(function(a, b){
return a.priority - b.priority;
});
$.each(sorter, function(){
tasks.append(this.el);
});
});
With no jquery:
<ul id="tasks">
<li id="item3" class="priority low"><span></span><span>This is a low priority task</span></li>
<li id="item4" class="priority high"><></span><span>This is a high priority task</span></li>
<li id="item5" class="priority low"><span></span><span>This is another Low</span></li>
<li id="item7" class="priority medium"><span></span><span>And now a Medium</span></li>
</ul>
<script type="text/javascript">
var tasks = document.getElementById("tasks");
var liElements = tasks.getElementsByTagName("li");
var lowPriority = [];
var mediumPriority = [];
var highPriority = [];
var removal = [];
for (var i = 0, len = liElements.length; i < len; i++) {
if (liElements[i].getAttribute("class").indexOf("low") > -1) lowPriority.push(liElements[i].cloneNode(true));
if (liElements[i].getAttribute("class").indexOf("medium") > -1) mediumPriority.push(liElements[i].cloneNode(true));
if (liElements[i].getAttribute("class").indexOf("high") > -1) highPriority.push(liElements[i].cloneNode(true));
removal.push(liElements[i]);
}
for (var i = 0, len = removal.length; i < len; i++ ) {
var liItem = removal[i];
liItem.parentNode.removeChild(liItem);
}
for( var i = 0, len = lowPriority.length; i < len; i++){
tasks.appendChild(lowPriority[i]);
}
for (var i = 0, len = mediumPriority.length; i < len; i++) {
tasks.appendChild(mediumPriority[i]);
}
for (var i = 0, len = highPriority.length; i < len; i++) {
tasks.appendChild(highPriority[i]);
}
</script>
Here's another jQuery–less option:
// Just a helper
function toArray(obj) {
var result = [];
for (var i=0, iLen=obj.length; i<iLen; i++) {
result[i] = obj[i];
}
return result;
}
// Uses querySelectorAll, but could use getElementsByTagName instead
function sortByPriority(id) {
var nodes;
var el = document.getElementById(id);
if (el) {
nodes = toArray(el.querySelectorAll('li.priority'));
nodes.sort(function(a, b) {
function getIndex(el) {
return el.className.indexOf('low') != -1? 1 :
el.className.indexOf('medium') != -1? 2 :
el.className.indexOf('high') != -1? 3 :
0; // default
}
return getIndex(b) - getIndex(a);
});
for (var i=0, iLen=nodes.length; i<iLen; i++) {
el.appendChild(nodes[i]);
}
}
}
It uses a few more lines that a jQuery (or perhaps any library) based solution but you don't have to load several thousand lines of library either.
Also, this runs about 5 times faster in Firefox and IE 9 and 10 times faster in Chrome than a jQuery solution (see http://jsperf.com/sortelementlist).
With pure JavaScript, and simple code!
var tasks = document.getElementById("tasks");
var lis = tasks.getElementsByTagName("li");
var lisarr = Array.prototype.slice.call(lis);
var priority = function(e){
var prio = {low: 0, medium: 1, high: 2};
return prio[e.getAttribute("class").match(/low|high|medium/)[0]];
};
lisarr.sort(function(a,b){
var ap = priority(a), bp = priority(b);
return bp - ap;
});
tasks.innerHTML = lisarr.reduce(function(prev, current){
return prev + current.outerHTML;
}, '');

javascript - shuffle HTML list element order

I have a list:
<ul>
<li>milk</li>
<li>butter</li>
<li>eggs</li>
<li>orange juice</li>
<li>bananas</li>
</ul>
Using javascript how can I reorder the list items randomly?
var ul = document.querySelector('ul');
for (var i = ul.children.length; i >= 0; i--) {
ul.appendChild(ul.children[Math.random() * i | 0]);
}
This is based on Fisher–Yates shuffle, and exploits the fact that when you append a node, it's moved from its old place.
Performance is within 10% of shuffling a detached copy even on huge lists (100 000 elements).
http://jsfiddle.net/qEM8B/
Simply put, like this:
JS:
var list = document.getElementById("something"),
button = document.getElementById("shuffle");
function shuffle(items)
{
var cached = items.slice(0), temp, i = cached.length, rand;
while(--i)
{
rand = Math.floor(i * Math.random());
temp = cached[rand];
cached[rand] = cached[i];
cached[i] = temp;
}
return cached;
}
function shuffleNodes()
{
var nodes = list.children, i = 0;
nodes = Array.prototype.slice.call(nodes);
nodes = shuffle(nodes);
while(i < nodes.length)
{
list.appendChild(nodes[i]);
++i;
}
}
button.onclick = shuffleNodes;
HTML:
<ul id="something">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<button id="shuffle" type="button">Shuffle List Items</button>
Demo: http://jsbin.com/itesir/edit#preview
var list = document.getElementById("something");
function shuffleNodes() {
var nodes = list.children, i = 0;
nodes = Array.prototype.sort.call(nodes);
while(i < nodes.length) {
list.appendChild(nodes[i]);
++i;
}
}
shuffleNodes();
Use this:
function htmlShuffle(elem) {
function shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
var el = document.querySelectorAll(elem + " *");
document.querySelector(elem).innerHTML = "";
let pos = [];
for (let i = 0; i < el.length; i++) {
pos.push(i);
}
pos = shuffle(pos);
for (let i = 0; i < pos.length; i++) {
document.querySelector(elem).appendChild(el[pos[i]]);
}
}
htmlShuffle("ul");
<ul>
<li>milk</li>
<li>butter</li>
<li>eggs</li>
<li>orange juice</li>
<li>bananas</li>
</ul>
Here is a very simple way to shuffle with JS:
var points = [40, 100, 1, 5, 25, 10];
points.sort(function(a, b){return 0.5 - Math.random()});
http://www.w3schools.com/js/js_array_sort.asp
I was searching for a prototype function. Maybe this helps someone.
Element.prototype.shuffleChildren = function() {
for (var i = this.children.length; i >= 0; i--) {
this.appendChild(this.children[Math.random() * i | 0]);
}
};
document.querySelector('body').shuffleChildren();
Here's a solution that does not use a loop.
function shuffle_children(element) {
element.append(...Array.from(element.children).sort(function () {
return Math.random() - 0.5;
}));
}
Based no #Alexey Lebedev's answer, if you prefer a jQuery function that shuffles elements, you can use this one:
$.fn.randomize = function(selector){
var $elems = selector ? $(this).find(selector) : $(this).children();
for (var i = $elems.length; i >= 0; i--) {
$(this).append($elems[Math.random() * i | 0]);
}
return this;
}
And then call it like this:
$("ul").randomize(); //shuffle all the ul children
$("ul").randomize(".item"); //shuffle all the .item elements inside the ul
$(".my-list").randomize(".my-element"); //shuffle all the .my-element elements inside the .my-list element.

Categories

Resources