I am creating a jquery plugin that will sort a list based on data attributes:
<ul class="reorder">
<li data-rating="1" data-category="3">Rating 1 - Category 3</li>
<li data-rating="5" data-category="1">Rating 5 - Category 1</li>
<li data-rating="2" data-category="2">Rating 2 - Category 2</li>
<li data-rating="7" data-category="1">Rating 7 - Category 1</li>
<li data-rating="21" data-category="3">Rating 21 - Category 3</li>
<li data-rating="19" data-category="2">Rating 19 - Category 2</li>
</ul>
As I don't know how many data attributes will be added, I want to be able to loop through the list and identify unique data attributes (NOT the value of those attributes) to create a set of links:
<ul class="reorder-nav">
<li>Sort by rating</li>
<li>Sort by category</li>
</ul>
My basic idea is to loop through each list item and create an array of data attributes, then filter that array for unique attributes.
I can create an array of all data attributes with .data() but other than that I'm a bit stuck so would appreciate any suggestions.
You can do something like this to extract the data-x attribute names :
var uniqueAttrs = {};
$('.reorder li').each(function(){
$.each(this.attributes, function(_,a){
if (a.name.indexOf('data-')===0) {
uniqueAttrs[a.name.slice(5)] = 1;
}
});
});
Then you can iterate over the keys of uniqueAttrs :
for (var attr in uniqueAttrs) {
console.log(attr); // for example "sort" or "ranking"
}
If you can afford to support a limited set of browsers, you can simplify the loop by using dataset instead of attributes.
Related
I have a simple list:
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" style="display: none">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
And need to get index of a specific item disregarding hidden items.
var list = document.getElementById('list');
var items = list.querySelectorAll('li:not([style*="display: none"])');
I try to convert NodeList in Array:
var list_items = Array.from(items);
But don't known how to run something like that: list_items.indexOf('item-3')
https://codepen.io/marcelo-villela-gusm-o/pen/RwNEVVB?editors=1010
You can make a function to find the id you need in a list you want, passing two parameters, that way you can use this function dynamically.
Based on id, inside the function just need to use .findIndex() that returns the index or -1 if not found.
See here:
var list = document.getElementById('list');
var items = list.querySelectorAll('li:not([style*="display: none"])');
var list_items = Array.from(items);
function getIndexById(idToSearch, list){
//ES6 arrow function syntax
return list.findIndex(elem => elem.id == idToSearch)
//normal syntax
//return list.findIndex(function(elem) {return elem.id == idToSearch})
}
console.log("found at index: ", getIndexById("item-3", list_items))
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" style="display: none">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
Not exactly related to the question, but if possible, I would suggest you to change your HTML to remove that inline style of display: none and change it to a class, (e.g: class='hidden'), it would be better for your .querySelector when using :not, for example: li:not(.hidden), since any space in your inline style can break your selector. ("display:none" != "display: none", spot the space)
Maybe like this:
var item = list_items.find(function(item) {
return item.id === "item-3";
});
I would recommend using :not(.hidden) instead of "grepping" for a match on the style tag. Then, simply find the index after casting the NodeList to an array.
For the Vue.js inclined, see this fiddle: https://jsfiddle.net/634ojdq0/
let items = [...document.querySelectorAll('#list li:not(.hidden)')]
let index = items.findIndex(item => item.id == 'item-4')
console.log('item-4 index in visible list is', index)
.hidden {
display: none;
}
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" class="hidden">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
Maybe you can use map. First you can create an object with id and value. Then use map function to create array of this object. Then you can access it with foreach, when id = 'item-3'.
Friends have a problem.
When clicking a link from a menu, I need to generate below the selected sub-menu item.
So far I can send the request via ajax, and generate a sub-menu, but this sub-menu always appears in the first position:
HTML CODE (simple menu)
<ul>
<li>Item 1
<ul id="city"></ul>
</li>
<li>Item 2
<ul id="city"></ul>
</li>
<li>Item 3
<ul id="city"></ul>
</li>
<li>Item 4
<ul id="city"></ul>
</li>
<li>Item 5
<ul id="city"></ul>
</li>
</ul>
JS CODE:
$('.region_id').on('click', function(event) {
event.preventDefault();
$.get('{!! url("filter_city") !!}', {id : $(this).attr('data-id'), token: $('input[name="_token"]').val() }, function(data) {
var cities = $('#city');
cities.empty();
$.each(data, function(key, value) {
cities.append($("<li></li>").text(value));
});
});
});
Result I get when clicking any option
As I can achieve what I want? Greetings from Chile
Identifier in HTML must be unique. You can use a common class and then traverse DOM using various methods.
Here's an example, I have used city as CSS class instead of ID. then the relevant element can be identified using any of these methods.
var cities = $(this).next('.city');
//var cities = $(this).siblings('.city');
//var cities = $(this).closest('li').find('.city');
HTML
<ul>
<li>
Item 1
<ul class="city"></ul>
</li>
</ul>
Script
$('.region_id').on('click', function(event) {
event.preventDefault();
var cities = $(this).next('.city');
$.get('{!! url("filter_city") !!}',
{
id : $(this).data('id'),
token: $('input[name="_token"]').val()
},
function(data) {
cities.empty();
$.each(data, function(key, value) {
cities.append($("<li></li>").text(value));
});
}
);
});
I would recommend you to use .data() instead of attr() to fetch data-* custom attribute value.
var id = $(this).data('id');
I have an array, it contains values [1,2].
I also have an html list
<ul>
<li id="1">List Item 1</li>
<li id="2">List Item 2</li>
<li id="3">List Item 3</li>
</ul>
I need to iterate through the array, and if a value in the array matches an ID in my list, add a class to the list item.
The output example would be
<ul>
<li id="1" class="active">List Item 1</li>
<li id="2" class="active">List Item 2</li>
<li id="3">List Item 3</li>
</ul>
I'm a bit lost on this one, thanks in advance!
Try to use $.map() to translate the array into "#1,#2" and pass it as a selector then add class to it,
var arr = [1,2]; // var arr = Express.completedSteps;
$($.map(arr,function(val,_){
return "#" + val;
}).join()).addClass('active');
DEMO
try
var arr=[1,2];
for(var i in arr){
$("#"+arr[i]).addClass("active");
}
DEMO
Try out following.
$(document).ready(function(){
var obj = [1,2,5,7];
$.each( obj, function( key, value )
{
$('li').each(function(){
if($(this).attr('id')==value)
{
$('#'+value).addClass( "active" );
}
});
});
})
try out the demo here.
demo
I have a list but when I try to generate autodividers for that list I'm getting duplicate dividers. Here is the code for the ul and the relevant script:
<div data-role="content">
<ul data-role="listview" id="ScheduleList" data-autodividers="true">
<li time="3:30PM">Event 1</li>
<li time="3:30PM">Event 2</li>
<li time="4:30PM">Event 3</li>
<li time="3:30PM">Event 4</li>
<li time="3:30PM">Event 5</li>
<li time="4:30PM">Event 6</li>
</ul>
</div>
</div>
<script>
$(document).on("pageinit", "#ScheduleDay", function(){
$("#ScheduleList").listview({
autodividers: true,
autodividersSelector: function (li) {
var out = li.attr('time');
return out;
}
}).listview('refresh');
});
</script>
Here is the code in JSFiddle: http://jsfiddle.net/4fGT6/65/
I know that I could reorder the list items in the html and that would eliminate the duplicate autodividers, but if I made the list to be generated dynamically from user inputs then I couldn't manually reorder the html.
Is there a way to solve this if the list had been generated dynamically?
Thanks.
First step, sort list items based on data-time attribute (I added data to facilitate reading values - data attribute is ignored by user agent, thus it won't mess up your code).
I used the below simple code, yet genius, made by #undefined.
Update:
Thanks to #Keir Lavelle for reviewing the code of sorting li elements.
var listview = $('#ScheduleList'),
listitems = listview.children('li');
listitems.detach().sort(function (a, b) {
var adata = $(a).data('time');
var bdata = $(b).data('time');
/* return (adata > bdata) ? (adata > bdata) ? 1 : 0 : -1; */
return (adata > bdata) ? 1 : -1;
});
listview.append(listitems);
Second step, apply autodividers dynamically.
$("#ScheduleList").listview({
autodividers: true,
autodividersSelector: function (li) {
var out = li.jqmData('time');
return out;
}
}).listview('refresh');
Demo
Credits to #undefined and #Keir Lavelle
I am attempting to sort a nested list using different variables. So, I have something like the following:
<div id="title">
<h2>AWARDS by TYPE</h2>
<span>
<p>Sort by: </p>
Trophy
Gold
Silver
Bronze
Other
</span>
</div>
And the lists:
<ul id="accolade-display-list">
<li class="accolade-display-list-item"> <img src="/images/us/law/accolades/organisation/27.jpg">
<ul>
<li class="accolades-org-name"> Argentina Wine Awards </li>
<li class="accolades-org-details">Silver Medal - 2012, Argentina</li>
</ul>
</li>
<li class="accolade-display-list-item"> <img src="/images/us/law/accolades/organisation/2.jpg">
<ul>
<li class="accolades-org-name"> Royal Adelaide Wine Show </li>
<li class="accolades-org-details">Regional Trophy - 2012, Argentina</li>
</ul>
</li>
<li class="accolade-display-list-item"> <img src="/images/us/law/accolades/organisation/57.jpg">
<ul>
<li class="accolades-org-name"> Wines of Chile Awards </li>
<li class="accolades-org-details"> Blue Gold Medal - 2012, Argentina</li>
</ul>
</li>
</ul>
What I would like to do is to be able to click, say "trophy" and sort the items with "trophy" at the top, then click "gold" and have those items together at the top, etc. There are currently one of each in my example, but there may be several "gold" items, "silver" items, and so on. I've tried a number of methods, but I have at the most been able to get the list to sort alphabetically, which is not what I need.
These are coming in from a JSP and I may be able to add additional classes to things as appropriate - I can also alter the list structure if necessary. The reason I have the nested lists is simply to make the alignment with the images easier, and because it is possible that there may be an additional line (<li>) in the future.
The way I've gotten the list to sort alphabetically, if it helps:
$("a.sort-by-trophy").click(function(){
console.log('sort-by-trophy clicked');
var list = $("ul#accolade-display-list");
var desc = false;
list.append(list.children().get().sort(function(a, b) {
var aProp = $(a).find(".accolades-org-name").text(),
bProp = $(b).find(".accolades-org-name").text();
return (aProp > bProp ? 1 : aProp < bProp ? -1 : 0) * (desc ? -1 : 1);
}));
});
Anyway, if anyone has any ideas as to something I might try, I would really appreciate it.
what i recommend is to have the values you should have, then generate the output (check underscore.js) and each time you click a sort, then generate it again and replace it.
what i mean, is that you havethe primivitve data:
var list = [{name: 'Argentina Wine Awards', medal: 'silver', ... }, {}, ...];
then, when is clicked a sort link, you sort the list array by the correct property and generate a new html (using underscore templates) and replace the old html with the new one.