Sort based on array - javascript

I am having issues on sorting based on array. Let's say I have something like this:
HTML:
<button onClick="sortfunc();">Sort</button>
<ul id="sortable">
<li id="a">is </li>
<li id="b">awesome</li>
<li id="c">very </li>
<li id="d">javascript </li>
<li id="e">hard </li>
<li id="f">but </li>
</ul>
Now I want to use an external button to sort it out and sort final value equal to d a c e f b order.
Javascript:
$(function() {
$( "#sortable" ).sortable();
});
function sortfunc() {
var idsInOrder = $("#sortable").sortable("toArray");
console.log(idsInOrder);
//Print: ["a", "b", "c", "d", "e", "f"]
var sorttoarray = ["d","a","c","e","f","b"];
//Do something here to sort the ul li to match sorttoarray
}
I print out the array but it is not in the order as expected.
Is it possible to use external button to sort out the above sortable to the id as indicated?
EDIT so id starts with letter.

Here's how you can accomplish this (I've altered the HTML a bit, as you don't need the jQuery UI sortable functionality):
<button id="doSomething">Sort</button>
<ul id="sortable">
<li id="1">is </li>
<li id="2">awesome</li>
<li id="3">very </li>
<li id="4">javascript </li>
<li id="5">hard </li>
<li id="6">but </li>
</ul>
And here's my JavaScript:
function sortfunc() {
var liItems = $("#sortable li");
var sorttoarray = ["4","1","3","5","6","2"];
$("#sortable").empty();
for (var i = 0; i < sorttoarray.length; i++) {
$("#sortable").append(liItems.filter("#" + sorttoarray[i]));
}
}
$("#doSomething").click(function () {
sortfunc();
});
Here's the working jsfiddle: http://jsfiddle.net/odmrxwxr/
Ultimately all I do here is save off the li elements, empty the parent ul, and then loop through the array for the order, and when I reach the next desired element that should be in the order, I just append() it back.

Related

Append array to a class in javascript

I have an array in javascript called menuElm that has <ul> elements in it:
<ul id="1"></ul>
<ul id="2"></ul>
<ul id="3"></ul>
I have a page in HTML that has the following:
<ul id="menu">
<li class="menu-item"></li>
<li class="menu-item"></li>
<li class="menu-item"></li>
</ul>
I want to add the elements of menuElm to the HTML page so it would look like this:
<ul id="menu">
<li class="menu-item">
<ul id="1"></ul>
</li>
<li class="menu-item">
<ul id="2"></ul>
</li>
<li class="menu-item">
<ul id="3"></ul>
</li>
</ul>
I have tried the following, but the <ul> elements just wont show up in the page nor in the code:
function CreateMenu() {
var menuElm;
var k = 0;
menuElm = createElm("ul");
menuElm.id = ++k;
for (var i = 0; i < menuElm.length; ++i) {
document.getElementsByClassName("menu-item")[i].appendChild(menuElm[i]);
}
}
I am new with JavaScript, what am I doing wrong?
menuElm.length
The ul element doesn't have a length, so you are looping from 0 to 0, which is 0 iterations.
menuElm = createElm("ul");
This function isn't defined. You need document.createElement('ul');
menuElm = createElm("ul");
menuElm.id = ++k;
You appear to be creating one list item, and then changing its ID and appending it multiple times.
You need a new list item each time you go around the loop.
appendChild(menuElm[i]);
You've been treating menuElm as an element previously. It isn't an array, [i] makes no sense here.
$("#menu").find('li').each(function(i){
$(this).append(menuElm[i]);
});
/* if you want to use jquery here is the code to append */

Dynamic heading text based upon selected link

I have a portfolio site set up and I have my portfolio set up to shuffle through by selecting certain filters. What I want to do is set some header text, based upon which filter they choose.
The issues is when I select a link, let's say advertising, Advertising will show up as my header text. However if I select something else, say branding, it doesn't change, it stays at advertising.
here is my html
<div id="portfolio-filter-container">
<h2 class="workFilterSelect">FILTER OPTIONS: <span class="currentFilter">ALL</span></h2>
</div>
<ul id="portfolio-filter">
<!--<li class="workFilterSelect">FILTER OPTIONS:</li>-->
<li class="workFilterButtons">All</li>
<li class="workFilterButtons">Advertising</li>
<li class="workFilterButtons">Branding</li>
<li class="workFilterButtons">Catalog</li>
<li class="workFilterButtons">Corporate ID</li>
<li class="workFilterButtons">Consumer Relations</li>
<li class="workFilterButtons">Incentive/Loyalty</li>
<li class="workFilterButtons">Packaging</li>
<li class="workFilterButtons">Product Launch</li>
<li class="workFilterButtons">Promotion</li>
<li class="workFilterButtons">Public Relations</li>
<li class="workFilterButtons">Sales Support</li>
<li class="workFilterButtons">Social Media</li>
<li class="workFilterButtons">Tradeshows</li>
<li class="workFilterButtons">Web/Mobile</li>
</ul>
<div id="bottomWrapper">
and here is my script
$(document).ready(function(){
$(".workFilterButtons a").click(function(){
$(".currentFilter").replaceWith($(this).append());
});
});
by default the page is set to "ALL" when loaded, but as you can see I am trying to gett he dynamic text to work within the .currentFilter span
Any help would be greatly appreciated.
If you only want text you use text() method to both get and set. Also you don't want to replace the element or it won't be found again because it will no longer exist
Try
$(".workFilterButtons a").click(function(){
$(".currentFilter").text($(this).text());
});
You don't need to replace the element with the clicked <a> element (unless you really want to), all you need to do is update the text:
$(document).ready(function () {
$(".workFilterButtons a").click(function () {
// selecting the element with the class
// of 'currentFilter', and setting its
// text (using the text() method) to
// the textContent of the clicked <a>
// element:
$(".currentFilter").text(this.textContent);
});
});
$(document).ready(function() {
$(".workFilterButtons a").click(function() {
$(".currentFilter").text(this.textContent);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="portfolio-filter-container">
<h2 class="workFilterSelect">FILTER OPTIONS: <span class="currentFilter">ALL</span></h2>
</div>
<ul id="portfolio-filter">
<!--<li class="workFilterSelect">FILTER OPTIONS:</li>-->
<li class="workFilterButtons">All
</li>
<li class="workFilterButtons">Advertising
</li>
<li class="workFilterButtons">Branding
</li>
<li class="workFilterButtons">Catalog
</li>
<li class="workFilterButtons">Corporate ID
</li>
<li class="workFilterButtons">Consumer Relations
</li>
<li class="workFilterButtons">Incentive/Loyalty
</li>
<li class="workFilterButtons">Packaging
</li>
<li class="workFilterButtons">Product Launch
</li>
<li class="workFilterButtons">Promotion
</li>
<li class="workFilterButtons">Public Relations
</li>
<li class="workFilterButtons">Sales Support
</li>
<li class="workFilterButtons">Social Media
</li>
<li class="workFilterButtons">Tradeshows
</li>
<li class="workFilterButtons">Web/Mobile
</li>
</ul>
<div id="bottomWrapper"></div>
JS Fiddle demo.
Incidentally, the reason your original code didn't work, and couldn't work, is because of this line:
$(".currentFilter").replaceWith($(this).append());
This replaced the selected element(s) with the clicked <a> element, which meant that, in future, the there was no .currentFilter element to replace or update.
On the other hand, if you want to put the clicked <a> element into the .currentFilter span-element, then you could try the following approach:
$(document).ready(function () {
$(".workFilterButtons a").click(function () {
// finding those <li> elements whose text, when trimmed
// (removing leading and trailing white-space) is equal
// to an empty string (''):
var emptyLi = $('.workFilterButtons').filter(function () {
return $(this).text().trim() === '';
}),
// caching the '.currentFilter' element(s):
currentFilter = $('.currentFilter'),
// checking for those elements in the
// currentFilter jQuery object that have
// a descendant <a> element, and finding
// length of that collection, and then
// checking that it's greater than 0:
hasA = currentFilter.has('a').length > 0;
// appending the contents of the currentFilter
// element into the found emptyLi element:
emptyLi.append(currentFilter.contents());
// if there are no <a> elements in the
// currentFilter element(s):
if (!hasA) {
// we replace the contents (textNode 'all')
// with the clicked <a> element:
currentFilter.contents().replaceWith(this);
} else {
// otherwise we append the clicked link to
// the currentFilter; this works because
// once we get to this stage the <a> element
// if it exists has already been moved back
// to the empty <li>, therefore we can't
// use '.contents().replace()' because
// there are no contents remaining by this point
// (and yes, this was incredibly counter-intuitive
// to me for quite a long time, which is why this
// update took a while):
currentFilter.append(this);
}
});
});
$(document).ready(function() {
$(".workFilterButtons a").click(function() {
var emptyLi = $('.workFilterButtons').filter(function() {
return $(this).text().trim() === '';
}),
currentFilter = $('.currentFilter'),
hasA = currentFilter.has('a').length > 0;
emptyLi.append(currentFilter.contents());
if (!hasA) {
currentFilter.contents().replaceWith(this);
} else {
currentFilter.append(this);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="portfolio-filter-container">
<h2 class="workFilterSelect">FILTER OPTIONS: <span class="currentFilter">ALL</span></h2>
</div>
<ul id="portfolio-filter">
<!--<li class="workFilterSelect">FILTER OPTIONS:</li>-->
<li class="workFilterButtons">All
</li>
<li class="workFilterButtons">Advertising
</li>
<li class="workFilterButtons">Branding
</li>
<li class="workFilterButtons">Catalog
</li>
<li class="workFilterButtons">Corporate ID
</li>
<li class="workFilterButtons">Consumer Relations
</li>
<li class="workFilterButtons">Incentive/Loyalty
</li>
<li class="workFilterButtons">Packaging
</li>
<li class="workFilterButtons">Product Launch
</li>
<li class="workFilterButtons">Promotion
</li>
<li class="workFilterButtons">Public Relations
</li>
<li class="workFilterButtons">Sales Support
</li>
<li class="workFilterButtons">Social Media
</li>
<li class="workFilterButtons">Tradeshows
</li>
<li class="workFilterButtons">Web/Mobile
</li>
</ul>
<div id="bottomWrapper"></div>
JS Fiddle demo.
References:
JavaScript:
Node.textContent.
String.prototype.trim().
jQuery:
append().
click().
contents().
filter().
has().
replaceWith().
text().
I changed your class="currentFilter" to id="currentFilter" so now you won't need a .each() to do what you want to do, the selector selects only 1 element and not an array of 1 element.
Also changed replaceWith() with text(), and likewise append() replaced with text().
Here's a fiddle if you want to see it in action
https://jsfiddle.net/s6bpwycn/

JavaScript: splitting an HTML list in half

I have a 26-item list with one for each letter of the alphabet
<ul>
<li id="a">A</li>
...
<li id="m">M</li>
<li id="n">N</li>
...
<li id="z">Z</li>
</ul>
How could I use JavaScript to terminate the list after 'M' (i.e. </ul>, add an <h2> element and start another list, beginning with 'N'?
So:
<ul>
...
<li id="m">M</li>
</ul>
<h2>Part 2</h2>
<ul>
<li id="n">N</li>
...
</ul>
I can insert the h2 element fine with .createElement and .insertBefore, but I can't get the closing and opening list tags either side of it. I've tried innerHTML, outerHTML, insertBefore...
The array#slice method doesn't work on DOM elements. We will have to iterate through all of the elements, and create a new array instead.
First we have to remove the original list from the DOM. We can do that with the removeChild function
var originalList = document.getElementById("existingList");
document.body.removeChild(orginalList);
# Or wherever your list was nested under
# find all your list elements. This is assuming you only have 26 list elements
var li = document.getElementsByTagName("li");
var firstLetters, secondLetters = [], [];
# Create array for first half of list elements
for (var i = 0; i < li.length/2; i++) {
firstLetters.push(li[i]);
}
# Create array for second half of list elements
for (var i = li.length/2; i < li.length; i++) {
secondLetters.push(li[i]);
}
var list1 = document.createElement("ul");
var list2 = document.createElement("ul");
document.body.appendChild(list1);
document.body.appendChild(list2);
for (var i = 0; i < firstLetters.length; i++) {
list1.appendChild(firstLetters(i));
}
for (var i = 0; i < secondLetters.length; i++) {
list2.appendChild(secondLetters(i));
}
You can get the ul, clone it, insert the clone, and append the childnodes of the ul after the 13th child element to the clone:
var ul = document.querySelector('ul'),
newUl = ul.cloneNode(false),
last = ul.children[12];
ul.parentNode.insertBefore(newUl, ul.nextSibling);
ul.parentNode.insertBefore(document.createElement('h2'), newUl)
.appendChild(document.createTextNode('Part 2'));
while(last.nextSibling) newUl.appendChild(last.nextSibling);
var ul = document.querySelector('ul'),
newUl = ul.cloneNode(false),
last = ul.children[12];
ul.parentNode.insertBefore(newUl, ul.nextSibling);
ul.parentNode.insertBefore(document.createElement('h2'), newUl).appendChild(document.createTextNode('Part 2'));
while(last.nextSibling) newUl.appendChild(last.nextSibling);
<ul>
<li id="a">A</li>
<li id="b">B</li>
<li id="c">C</li>
<li id="d">D</li>
<li id="e">E</li>
<li id="f">F</li>
<li id="g">G</li>
<li id="h">H</li>
<li id="i">I</li>
<li id="j">J</li>
<li id="k">K</li>
<li id="l">L</li>
<li id="m">M</li>
<li id="n">N</li>
<li id="o">O</li>
<li id="p">P</li>
<li id="q">Q</li>
<li id="r">R</li>
<li id="s">S</li>
<li id="t">T</li>
<li id="u">U</li>
<li id="v">V</li>
<li id="w">W</li>
<li id="x">X</li>
<li id="y">Y</li>
<li id="z">Z</li>
</ul>

How to rename list ids according to current position using jquery .sortable()?

First of all I am sorry that I cannot explain the scenario as for my poor english.This is what actually I need to do.
I have a list as follows,
<ul id="sortable" class="sortable">
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
<li id="4">4</li>
</ul>
list IDs and their values are same.Normally,if I move list ID 4 to top using jquery sortable then I get the new list as,
<ul id="sortable" class="sortable">
<li id="4">4</li>
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
</ul>
But What I need to do is rename/reorder the IDs to Ascending as for current order of list.
which means
ID 4 => ID 1
ID 1 => ID 2
ID 2 => ID 3
ID 3 => ID 4
Is this possible using jQuery .sortable(). If so how would I overcome this problem?
Thanks a lot.
Yes, you can do it with any of a number of supported events. For this example I'll use stop:
$(".sortable").sortable({
stop: function(e, ui) {
$(this).children("li").each(function(idx, elt) {
$(elt).attr("id", idx);
});
}
});
However note that this (changing id-s) is generally considered bad practice, there should be a way for you to achieve your results without it. Also, if you're concerned with backward compatibility remember that id-s starting with a number are not part of the standard in HTML4.
You can sort like this
function sort_by_id(a, b) {
var sa = a.id, sb = b.id;
return sb>sa ? -1 : sb<sa ? 1 : 0;
}
function sortList(){
var list = $("#sortable > li").get();
list.sort(sort_by_id);
for (var i = 0; i < list.length; i++) {
list[i].parentNode.appendChild(list[i]);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
Sort list
<ul id="sortable" class="sortable">
<li id="4">4</li>
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
</ul>

Looking for elements with specific depth in JavaScript

I need to write a function in pure JavaScript witn no framework to get all specific tags, but only from first level under parent.
For example: I need to call some function on first <ul> and get all <li> from first level of it (<li> with text 1.2 and <li> with text 2.1)
<div id="sideNavigation">
<ul>
<li class=" act open ">
1.2
<ul>
<li class=" ">
1.2
<ul>
<li class=" ">
1.3
<ul>
<li class=" ">1.4</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class=" ">
2.1
<ul>
<li class=" ">2.2.1</li>
<li class=" ">2.2.2</li>
<li class=" ">2.2.3</li>
</ul>
</li>
</ul>
</div>
I've been trying to do it like this:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li");
but it returns all <li> in this div not only first level <li>. Do you have any quick method to solve my problem or do I have to implement a new function to detect depth of nodes
You can use the attribute .children to get those "li"
var firstDepthLi = document.getElementById("sideNavigation").children[0].children;
If you want a generic function you can create something like:
var getElementsByDepth = function(el, tagname, depth) {
var children = el.children;
var res = new Array();
for(var i=0; i<children.length; i++) {
if (children[i].tagName == tagname) {
res.push(children[i]);
if (depth > 0)
res.concat(getElementsByDepth(children[i], tagname, depth-1));
}
}
return res;
}
Try:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[0];
That should return the first li element out of all li's on the page. Change the zero at the end to a different number to get a different element. You could even set a variable for the value:
var liNum = 0;
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[liNum];
And in a function:
function getLi(depth) {
var specificLi = document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
return specificLi;
}
var firstLi = getLi(0);
console.log(firstLi);
<div id="sideNavigation">
<ul>
<li>First list tag</li>
<li>Second list tag</li>
<li>Third list tag</li>
</ul>
</div>
And to make the function even shorter, you could just do:
function getLi(depth) {
return document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
}
That should work. :)

Categories

Resources