Is it possible to sort a jQuery result by an attribute's value? For example, consider the following code snippet and result:
$('span').each(function()
{
$('#log').append($(this).attr('name'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<span name="a5"></span>
<span name="a3"></span>
<span name="a6"></span>
<span name="a1"></span>
<span name="a4"></span>
<div id="log"></div>
Without modifying the HTML or DOM, how can I best modify this code to get a sorted result, ie. a1a3a4a5a6? Though I could obviously stick each result in an array and then sort it afterwards, I'm assuming there's a more elegant way using jQuery's native abilities. Is there?
You don't need to build intermediate array, jQuery exposes Array.prototype.sort method for convenience:
$.fn.sort = Array.prototype.sort;
and since Array.prototype.sort; is generic (*) it just magically works with jQuery collections too.
$('span')
.sort(function(a, b) {
return $(a).attr('name').localeCompare($(b).attr('name'));
})
.each(function() {
$('#log').append($(this).attr('name'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span name="a5"></span>
<span name="a3"></span>
<span name="a6"></span>
<span name="a1"></span>
<span name="a4"></span>
<div id="log"></div>
* Generic in context of prototype methods means that methods internal implementation does not care about this instance being a real Array instance, as soon as it's an array-like collection.
Try with this code:
var $names = $('span');
var $log = $('#log');
$names.sort(function(a,b){
var aProp = a.getAttribute('name'),
bProp = b.getAttribute('name');
if (aProp > bProp){
return 1;
}
if (aProp < bProp){
return -1;
}
return 0;
});
$names.appendTo($log);
Related
I've written this bit of jQuery code in Oxygen Builder's JavaScript element to query the job board API and return an array of departments and their jobs. I'm testing to see if the department[0].jobs.length returns 0 then hide the #job-list div, otherwise show it and its associated jobs. The code succeeds in querying the API and returning 0 jobs but the remainder of the ternary operator will not hide the div.
jQuery(document).ready(function($) {
$.getJSON('https://boards-api.greenhouse.io/v1/boards/forwardnetworks/departments', postings => {
$("#div_block-420-61456").html(`
<div id="job-list">${postings.departments[0].jobs.length == 0 ? $("#job-list").hide() : $("#job-list").show()}<h3 class="dept">${postings.departments[0].name}</h3>
${postings.departments[0].jobs.map(item => `<h4 class="job-title">${item.title}</h4>
<p class="job-descrip">${item.location.name}`).join('')}</div> `);
});
});
I generally get a return of [object object]
As I mentioned in the comments, I would add a guard within the .getJSON success handler that will return early if there are no jobs to display.
The resulting function would be:
const departmentIndex = 0;
$(function ($) {
$.getJSON('https://boards-api.greenhouse.io/v1/boards/forwardnetworks/departments', postings => {
if (postings.departments[departmentIndex].jobs.length === 0) { return; }
$("#div_block-420-61456").html(`
<div id="job-list">
<h3 class="dept">${postings.departments[departmentIndex].name}</h3>
${postings.departments[departmentIndex].jobs.map(item => `
<a href="${item.absolute_url}">
<h4 class="job-title">${item.title}</h4>
</a>
<p class="job-descrip">${item.location.name}`
).join('')}
</div>
`);
});
});
Note: I put the index in a variable so that I could easily test with different departments.
Here is an example fiddle.
I am looping through elements using jQuery like this:
$(".myelement").each(function() {
$element = $(this).closest(".panel").attr("id");
console.log($element);
});
This is working correctly and I am seeing each of the elements it finds in my console log. I am now trying to get a string containing each element that looks like this:
#element1, #element2, #element3
What is the easiest way to do this? Does anybody have an example they can point me at?
You could use map() to build an array of the id then join() it, something like this:
var ids = $(".myelement").map(function() {
return '#' + $(this).closest(".panel").prop("id");
}).get().join(', ');
console.log(ids);
You could use an array to store them by adding the # in every iteration, then after the loop end join them using join() method like :
var ids = [];
$(".myelement").each(function() {
ids.push('#' + $(this).closest(".panel").attr("id"));
});
console.log(ids.join(', '));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="element1" class="panel">
<span class="myelement">My element 1</span>
</div>
<div id="element2" class="panel">
<span class="myelement">My element 2</span>
</div>
<div id="element3" class="panel">
<span class="myelement">My element 3</span>
</div>
Try with map()
The .map() method is particularly useful for getting or setting the value of a collection of elements.
As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array.
You can use map(), get() and join()
in the following way:
var $element = $(".myelement").map(function(){
return $(this).closest(".panel").attr("id");
}).get().join(', ');
console.log($element);
I've got a todo list. Each row has a star icon that you can click, exactly like gmail. The difference here is that if you click a star it should sort to the top (higher priority), but also re-sort within the starred group by ascending alpha. Unstarred items sort below, also sorted by ascending alpha. Everything is working as expected except for the alpha sorting. Below is the sort function where I'm doing that. I've verified that everything works below except the //sort the arrays by alpha bit...
Sort fail:
function sortTasks(currList) {
var starredTasks = [];
var unstarredTasks = [];
//create arrays
$('li.task').each(function(){
if ($(this).children('img.star').attr('src') == "images/star_checked.gif") {
starredTasks.push($(this));
} else {
unstarredTasks.push($(this));
}
});
//sort the arrays by alpha
starredTasks.sort( function(a,b){ ($(a).children('p.task-name').text().toUpperCase() > $(b).children('p.task-name').text().toUpperCase()) ? 1 : -1;});
unstarredTasks.sort( function(a,b){ ($(a).children('p.task-name').text().toUpperCase() > $(b).children('p.task-name').text().toUpperCase()) ? 1 : -1;});
//draw rows starred first, unstarred second
$(currList).empty();
for (i=0; i < starredTasks.length; i++) {
$(currList).append(starredTasks[i]);
}
for (i=0; i < unstarredTasks.length; i++) {
$(currList).append(unstarredTasks[i]);
}
}
This array has been populated with the task rows in the order they were originally drawn. The data renders fine, but basically stays in the same order.
Example task row:
<div id="task-container" class="container">
<form name="enter-task" method="post" action="">
<input id="new-task" name="new-task" type="text" autofocus>
</form>
<h2 id="today">today</h2>
<ul id="today-list">
<li id="457" class="task">
<img class="star" src="images/star_checked.gif">
<p class="task-name" contenteditable>buy milk</p>
<p class="task-date"> - Wednesday</p>
</li>
</ul>
<h2 id="tomorrow">tomorrow</h2>
<ul id="tomorrow-list">
</ul>
<h2 id="future">future</h2>
<ul id="future-list">
</ul>
<h2 id="whenever">whenever</h2>
<ul id="whenever-list">
</ul>
</div>
Each item in the starredTasks array is an entire task row. I'm assuming that $(a) is the same level as $(li)?
and here's the function that triggers the sort:
$('body').on('click', 'img.star', function(){
var thisList = '#' + $(this).parent('li').parent('ul').attr('id');
if ($(this).attr('src') == 'images/star_checked.gif') {
$(this).attr('src', 'images/star_unchecked.gif');
} else {
$(this).attr('src', 'images/star_checked.gif');
}
sortTasks(thisList);
});
Also, I doubt it's worth mentioning, but the data is stored in mySQL and prepopulated via php.
I wasn't sure of a way to use .sort() directly on the $('li') without splitting it into separate arrays...
Anybody see my goof?
I don't see where you're adding the sorted list back into the DOM. If you're not, then that's the problem. Sorting an array of elements doesn't update the DOM at all.
Furthermore, your sorting is very expensive. It's better to map an array of objects that have the elements paired with the actual values to sort.
Finally, you appear to be using the same ID multiple times on a page. That's just wrong. it may work with jQuery's .children(selector) filter, but it's still wrong. You need to change that.
Here I map an array of objects that contain a text property holding the text to sort and a task property that holds the element.
I changed p#task-name to p.task-name, so you should change that to class="task-name" on the elements.
Then I do the sort using .localeCompare(), which returns a numeric value.
Finally, the .forEach() loop appends the elements to the DOM.
var data = starredTasks.map(function(t) {
return { task: t,
text: $(t).children('p.task-name').text().toUpperCase()
};
}).sort(function(obj_a, obj_b) {
obj_a.text.localeCompare(obj_b.text);
}).forEach(function(obj) {
original_container.append(obj.task);
});
This assumes starredTasks is an actual Array. If it's a jQuery object, then do starredTasks.toArray().map(func....
The original_container represents a jQuery object that is the direct parent of the task elements.
Lets say I had the following code:
<div class="post">
<h2 itemprop="name">
The Post Title
</h2>
<div class="details">
<span>
<em class="date">Jul 17, 2014 </em>
</span>
<span>
Category:
Staff Profile
</span>
</div>
How would I possibly get the values of "The Post Title" and "Staff Profile" using JavaScript without changing the HTML on the page at all? i.e. I couldn't use getElementbyID for example. I could use jQuery if I had to but would rather not if possible.
You can get these values using getElementsByTagName which returns an array
document.getElementsByTagName("a")[0].innerHTML // returns The Post Title
document.getElementsByTagName("a")[1].innerHTML // returns Staff Profile
If these links are the first ones you can use indexes 0 and 1, otherwise you should look for the right index
Update
Another way that may be simple is to select these links inside the div with the class post
var links = document.getElementsByClassName("post")[index].getElementsByTagName("a");
links[0].innerHTML; // returns The Post Title
links[1].innerHTML; // returns Staff Profile
This solution would be the best one if the index of the div with the class post doesn't change
For a jQuery based expression you can use this:
$('a').map(function() {
return [this.href, this.textContent];
}).get();
which should return:
[ [ 'http://www.example.com', 'The Post Title' ],
[ 'http://sitename/category/staff-profile/', 'Staff Profile' ] ]
Should you specifically want the original relative URLs instead of the normalised full URLs, use this.getAttribute(href) in place of this.href
For a pure (ES5) equivalent:
[].map.call(document.getElementsByTagName('a'), function (el) {
return [el.href, el.textContent];
});
Older browsers that don't support the W3C standard .textContent property may require the .innerText property instead, e.g.:
return [el.href, el.textContent || el.innerText];
You can do:
var posts = document.querySelector('.post');
for (var i = 0; i < posts.length; i++) {
var links = document.querySelectorAll('a');
var title = links[0].innerText || links[0].textContent;
var profile = links[1].innerText || links[1].textContent;
}
If you are using a more modern browser, you can use document.querySelectorAll() which takes in CSS style selector syntax.
var aList = document.querySelectorAll('.post a');
for (var i = 0; i < aList.length; ++i) {
alert(aList[i].innerHTML);
}
JSFiddle
I used '.post a' rather than just 'a' because I assume your page may have other 'a' tags in it that you don't necessarily want.
I have a div, #containerDiv, which contains elements related to users like first name, last name etc. in separate divs. I need to sort the contents of the container div based on the last name, first name etc. values.
On searching google the examples I got all are appending the sorted results and not changing the entire HTML being displayed. They are also not sorting by specific fields (first name, last name).
So please help me in sorting the entire content of #containerDiv based on specific fields and also displaying it.
The Page looks Like something as mentioned Below:
<div id="containerDiv">
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
</div>
On getting sorted by last name div values, the resulted structure of the container div should look like:
<div id="containerDiv">
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
</div>
Now I think you all can help me in a better way.
this is a sample example:
html:
<div id="containerDiv">
<div>2</div>
<div>3</div>
<div>1</div>
</div>
js
$(function() {
var container, divs;
divs = $("#containerDiv>div").clone();
container = $("#containerDiv");
divs.sort(function(divX, divY) {
return divX.innerHTML > divY.innerHTML;
});
container.empty();
divs.appendTo(container);
});
you may set your divs.sort function param depend on your goal.
jsFiddle.
and a jQuery Plugin is suitable
I suggest you read the div values so you get an array of objects (persons for example) or just names and perform a sort operation on that. Than...output the result to the initial div (overwriting the default values).
I have built a jQuery sort function in which you can affect the sort field.
(it rebuilds the html by moving the row to another location).
function sortTableJquery()
{
var tbl =$("#tbl tr");
var store = [];
var sortElementIndex = parseFloat($.data(document.body, "sortElement"));
for (var i = 0, len = $(tbl).length; i < len; i++)
{
var rowDom = $(tbl).eq(i);
var rowData = $.trim($("td",$(rowDom)).eq(sortElementIndex).text());
store.push([rowData, rowDom]);
}
store.sort(function (x, y)
{
if (x[0].toLowerCase() == y[0].toLowerCase()) return 0;
if (x[0].toLowerCase() < y[0].toLowerCase()) return -1 * parseFloat($.data(document.body, "sortDir"));
else return 1 * parseFloat($.data(document.body, "sortDir"));
});
for (var i = 0, len = store.length; i < len; i++)
{
$("#tbl").append(store[i][1]);
}
store = null;
}
Every time I need to sort lists I use ListJs.
It's well documented, has good performance even for large lists and it's very lightweight (7KB, despite being library agnostic).