Sorting array by HTML data attribute? - javascript

I'm trying to learn local storage. I have five links each with a data-date attribute and I want them to be sorted using that attribute. I've tried numerous ways but nothing seems to work. From what I understand, I should parse before sorting but it didn't work for me. I must have done it wrong because I don't see how else to do it.
Here is my HTML:
<div id="div1">
<input id='clearHistory' type='button' value='Remove All History' />
<input id='showHistory' type='button' value='Show History' />
<ul id='history'>
<li>
<a class='date' href="#aa" data-date="November 12, 2001 03:24:00">Link 1</a>
</li>
<li>
<a class='date' href="#bb" data-date="August 4, 1993 03:24:00">Link 2</a>
</li>
<li>
<a class='date' href="#cc" data-date="October 17, 1995 03:24:00">Link 3</a>
</li>
<li>
<a class='date' href="#dd" data-date="December 1, 2010 03:24:00">Link 4</a>
</li>
<li>
<a class='date' href="#ee" data-date="August 17, 2004 03:24:00">Link 5</a>
</li>
</ul>
</div>
<p>Click on 'Show History' to see the 'user history'.</p>
<ul id='storedHistory'></ul>
And my JavaScript:
$(document).ready(function() {
var storedHistory = document.getElementById('storedHistory');
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
};
Storage.prototype.getObject = function(key) {
var value = this.getItem(key);
return value && JSON.parse(value);
};
//function sortDatesAscending(a, b) { return a.valueOf() - b.valueOf(); } function sortDatesDescending(a, b) { return b.valueOf() - a.valueOf(); }
function sortLinkDatesAscending(obj1, obj2) {
return obj1.date.valueOf() - obj2.date.valueOf();
}
function sortLinkDatesDescending(obj1, obj2) {
return obj2.date.valueOf() - obj1.date.valueOf();
}
var history = {
items: []
};
// Get each anchor and retrieve its date and link. Add to an object and add that object to the history's array Sort by ascending. Add to local storage.
$('ul > li > a').click(function(e) {
var date = $(this).attr('data-date');
var listData = {
link: $(this).attr("href"),
date: date
};
history.items.push(listData);
window.localStorage.setObject("history", history);
});
/* Remove items from local storage */
$('#clearHistory').click(function() {
window.localStorage.clear();
});
/* Retrieve items from local storage and add to stored history unordered list */
$('#showHistory').click(function() {
console.log(window.localStorage);
var listHistory = localStorage.getObject('history');
var counter = 1;
for (var i = 0; i < listHistory.items.length; i++) {
$("#storedHistory").append('<li>' + counter + '. Link: ' + listHistory.items[i].link + '<br>' + 'Date: ' + listHistory.items[i].date + '</li>');
counter++;
}
});
});
And here is the jsfiddle: https://jsfiddle.net/fLLsfd5j/2/

Try this for sorting! (http://trentrichardson.com/2013/12/16/sort-dom-elements-jquery/)
var $history = $('#history li'),
$historyA = $history.children();
$historyA.sort(function (a, b) {
var an = Date.parse(a.getAttribute('data-date')).valueOf(),
bn = Date.parse(b.getAttribute('data-date')).valueOf();
if (an > bn) {
return 1;
}
if (an < bn) {
return -1;
}
return 0;
});
$('#history').empty();
$.each($historyA, function () {
$('#history').append($('<li>').html(this));
});

I guess this should do your job
function getHistory(){
var as = document.querySelectorAll(".date"); // get elements with date class
Array.prototype.map.call(as, e => e.cloneNode(true)) //clone them into an array and sort
.sort((p,c) => Date.parse(p.dataset.date)<=Date.parse(c.dataset.date) ? -1 : 1)
.forEach((e,i) => as[i].parentNode.replaceChild(e, as[i]));
}
showHistory.onclick = getHistory; //add "click" eL to the DOM element with showHistory id
http://jsbin.com/yoraqusora/2/edit?js,console,output

Related

How to pass a list/array from thymleaf's th:onclick method to a javascript function

Here is what I am trying to do:
<ul id="loadavg_ul">
<a href="#" th:each="LoadAvgCoordinates:${LoadAvgCoordinatesList}"
th:data1=${LoadAvgCoordinates.subDir}
th:data-xaxis=${LoadAvgCoordinates.xaxis} <<< this is an array/list
th:data-yaxis=${LoadAvgCoordinates.yaxis} <<< this too is an array/list
th:onclick="javascript:drawLoadavg(this.getAttribute('data1'),this.getAttribute('data-xaxis'),
this.getAttribute('data-yaxis'))">
<li th:text="${LoadAvgCoordinates.subDir}">LoadAvgCoordinate.subDir</li>
</a>
</ul>
Javascript Fucntion:
function drawLoadavg(subDir, xaxis, yaxis) {
document.getElementById("landing_zone").innerHTML =
"Drawing loadavg for sub dir: " + subDir;
var ul = document.createElement("ul");
ul.setAttribute("id", "proList");
document.getElementById("renderList").appendChild(ul);
for (var i = 0; i < xaxis.length; i++) {
renderXAxis(xaxis[i]);
console.log(xaxis[i]);
}
//xaxis.forEach(renderXAxis); <<< same prob
function renderXAxis(element, index, arr) {
var li = document.createElement("li");
li.setAttribute("class", "item");
ul.appendChild(li);
li.innerHTML = li.innerHTML + element;
}
}
Actual array is:
xaxis = [
"2021-05-24 17:04:31",
"2021-05-24 17:07:44",
"2021-05-24 17:13:06",
"2021-05-24 17:25:52",
"2021-05-24 17:27:58",
];
&
yaxis = [6.46, 10.97, 10.33, 6.42, 6.11];
but in console it gives:
[
"
2
...
2
4 << seems to be space delimited**
I personally wouldn't try passing JavaScript information into data attributes. Just let Thymeleaf serialize it and process it from there. For example, this worked for me:
<script th:inline="javascript">
var coordinateList = [[${LoadAvgCoordinatesList}]];
</script>
<ul id="loadavg_ul">
<a href="#" th:each="LoadAvgCoordinates, s:${LoadAvgCoordinatesList}"
th:data-index="${s.index}"
onclick="javascript:drawLoadavg(
coordinateList[parseInt(this.getAttribute('data-index'))].subDir,
coordinateList[parseInt(this.getAttribute('data-index'))].xaxis,
coordinateList[parseInt(this.getAttribute('data-index'))].yaxis
)">
<li th:text="${LoadAvgCoordinates.subDir}">LoadAvgCoordinate.subDir</li>
</a>
</ul>

Creating search bar

I am trying to make a pure JavaScript search box. I want it to search very loosely, i.e. it shouldn't be case sensitive, it shouldn't take spaces into account, etc.
I came upon this codepen, which has what I'm looking for, but it's written using JQuery. I'm trying to convert it to JavaScript only, but I'm having trouble doing it.
My question isn't on the animation, it's on the bare minimum search code. So basically here's the only part I need help with:
$.extend($.expr[':'], {
'containsi': function(elem, i, match, array) {
return (elem.textContent || elem.innerText || '').toLowerCase()
.indexOf((match[3] || "").toLowerCase()) >= 0;
}
});
var searchSplit = searchTerm.replace(/ /g, "'):containsi('")
$("#list li").not(":containsi('" + searchSplit + "')").each(function(e) {
$(this).addClass('hidden');
});
$("#list li:containsi('" + searchSplit + "')").each(function(e) {
$(this).removeClass('hidden');
});
I got the JavaScript equivalent of JQuery's extend which is in the JSFiddle bellow.
JSFiddle
$(document).ready(function() {
// My JavaScript code
var input = document.getElementById('search-text');
input.addEventListener("keyup", function() {
var searchTerm = input.value,
listItem = document.getElementsByClassName('searchArray');
// JavaScript equivalent of jQuery's extend method
function extend(a, b) {
for (var key in b)
if (b.hasOwnProperty(key))
a[key] = b[key];
return a;
}
});
// Bare minimum code from http://codepen.io/robooneus/pen/ivdFH
//we want this function to fire whenever the user types in the search-box
$("#search-text").keyup(function() {
//first we create a variable for the value from the search-box
var searchTerm = $("#search-text").val();
//then a variable for the list-items (to keep things clean)
var listItem = $('#list').children('li');
//extends the default :contains functionality to be case insensitive
//if you want case sensitive search, just remove this next chunk
$.extend($.expr[':'], {
'containsi': function(elem, i, match, array) {
return (elem.textContent || elem.innerText || '').toLowerCase()
.indexOf((match[3] || "").toLowerCase()) >= 0;
}
}); //end of case insensitive chunk
//this part is optional
//here we are replacing the spaces with another :contains
//what this does is to make the search less exact by searching all words and not full strings
var searchSplit = searchTerm.replace(/ /g, "'):containsi('")
//here is the meat. We are searching the list based on the search terms
$("#list li").not(":containsi('" + searchSplit + "')").each(function(e) {
//add a "hidden" class that will remove the item from the list
$(this).addClass('hidden');
});
//this does the opposite -- brings items back into view
$("#list li:containsi('" + searchSplit + "')").each(function(e) {
//remove the hidden class (reintroduce the item to the list)
$(this).removeClass('hidden');
});
});
});
.hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="search-text" placeholder="search">
<ul id="list">
<li class="searchArray">Apple pie</li>
<li class="searchArray">Pumpkin pie</li>
<li class="searchArray">Banana-creme pie</li>
<li class="searchArray">Peach-blackberry cobbler</li>
<li class="searchArray">Chocolate-strawberry torte</li>
<li class="searchArray">Chocolate-zucchini cake</li>
<li class="searchArray">Anything involving chocolate and mint</li>
<li class="searchArray">Red-velvet cake</li>
<li class="searchArray">Anything involving fruits that aren't cherries</li>
</ul>
You can use a version of typeahead.js substringMatcher function
var substringMatcher = function(strs, q, cb) {
return (function(q, cb, name) {
var matches, substrRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
strs.forEach(function(str, i) {
if (substrRegex.test(str)) {
// the typeahead jQuery plugin expects suggestions to a
// JavaScript object, refer to typeahead docs for more info
matches.push(name(str));
}
});
cb(matches);
}(q, cb, function(res) {
return res
}));
};
var elems = document.getElementsByClassName('searchArray');
// My JavaScript code
var listItem = Array.prototype.slice.call(elems);
var list = listItem.map(function(el) {
el.className = el.className + " " + "hidden";
return el.textContent;
})
var input = document.getElementById('search-text');
input.addEventListener("keyup", function() {
var searchTerm = this.value;
if (searchTerm.length === 0) {
listItem.forEach(function(el, i) {
el.className = el.className + " " + "hidden"
});
return false
}
substringMatcher(list, searchTerm, function(results) {
results.forEach(function(value, index) {
if (list.indexOf(value) > -1) {
elems[list.indexOf(value)].className = elems[list.indexOf(value)]
.className.replace("hidden", "");
} else {
listItem.forEach(function(el, i) {
if (i !== list.indexOf(value)) {
el.className = el.className + " " + "hidden";
}
})
}
})
})
});
.hidden {
display: none;
}
<input type="text" id="search-text" placeholder="search">
<ul id="list">
<li class="searchArray">Apple pie</li>
<li class="searchArray">Pumpkin pie</li>
<li class="searchArray">Banana-creme pie</li>
<li class="searchArray">Peach-blackberry cobbler</li>
<li class="searchArray">Chocolate-strawberry torte</li>
<li class="searchArray">Chocolate-zucchini cake</li>
<li class="searchArray">Anything involving chocolate and mint</li>
<li class="searchArray">Red-velvet cake</li>
<li class="searchArray">Anything involving fruits that aren't cherries</li>
</ul>

Add and Remove class to click a dynamic Button

Trying to Add and Remove class to click dynamic Buttons, means this button <button class="one"></button> get class dynamically like this: <button class="one text1">text1</button>
So if button one has class .text1 and by click this add class .hide to list item <li class="text1"> like <li class="text1 show">
Same for button two <button class="two"></button> and by click add class <li class="text2 show">
Note: when click button two, then should remove class .show and add new class .hideto button one.
Main HTML:
<div id="main-id">
<button class="one"></button>
<button class="two"></button>
<ul>
<li>
<!--List 1-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 2 is Same-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 3 is different-->
<div class="label">
text2
</div>
</li>
</ul>
</div>
Script:
$('.label a').each(function() {
var $this=$(this);
$this.closest('li').addClass($this.text());
});
// Combine This
$('button').each(function(){
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function(){
if(clses.indexOf($(this).attr('class')) === -1){
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if(ind === liInd){
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
});
See Example on Fiddle
I have tried this by add/remove class by click function, but problem is Buttons get class dynamically from List items, so I'm not able to target button.
Any suggestion for other way to do this by JS/ Jquery?
Here is an alternative solution
$('button').each(function () {
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function () {
if (clses.indexOf($(this).attr('class')) === -1) {
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if (ind === liInd) {
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
if (txt != '') {
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
}
});
$('button').click(function () {
if ($(this).attr('class')[0] == 'all') {
showAll();
return false; // end this function
}
var allCls = $(this).attr('class').split(' ');
$('li').each(function () {
if (allCls.indexOf($(this).find('a').text()) > -1) {
$(this).closest('li').removeClass('show').addClass('hide');
} else {
$(this).closest('li').removeClass('hide').addClass('show');
}
});
});
function showAll() {
$('li').removeClass('hide').addClass('show');
}
Fiddle: https://jsfiddle.net/taleebanwar/yaLm4euk/13/
DEMO
$('.label a').each(function () {
var $this = $(this);
$this.closest('li').addClass($this.text());
});
// Combine This
$('button').each(function () {
var liInd = 0;
var cl = '';
var txt = '';
var clses = [];
var ind = $('button').index($(this)) + 1;
$('li').each(function () {
if (clses.indexOf($(this).attr('class')) === -1) {
clses.push($(this).attr('class'));
liInd = liInd + 1;
}
if (ind === liInd) {
cl = $(this).attr('class');
txt = $(this).find('a').text();
return false; //break
}
});
$('button:nth-child(' + ind + ')').addClass(cl);
$('button:nth-child(' + ind + ')').text(txt);
});
$(document).on('click', 'button',function(e){
var textClass = $.grep(this.className.split(" "), function(v, i){
return v.indexOf('text') === 0;
}).join();
console.log(textClass);
$('li').removeClass('show').addClass('hide')
$('li').each(function(){
if($(this).hasClass($.trim(textClass))){
$(this).removeClass('hide').addClass('show');
} else {
$(this).removeClass('show').addClass('hide');
}
})
})
.show{display:list-item;}
.hide{display:none;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="main-id">
<button class="one"></button>
<button class="two"></button>
<ul>
<li>
<!--List 1-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 2 is Same-->
<div class="label">
text1
</div>
</li>
<li>
<!--List 3 is different-->
<div class="label">
text2
</div>
</li>
</ul>
</div>

angularjs and scope issues (I think)

I am new to angular and I have been trying to make a pretty advanced directive.
Most of the directive works, but there are two issues presenting themselves currently and I think they are both related to the scope.
Here is my directive:
angular.module('test')
.directive('testKitDesigner', function () {
panels = [];
function bindEvents() {
console.log("bindingEvents");
var styledElements = ["piping", "panel-1", "panel-2", "panel-3", "panel-4", "panel-5", "panel-6", "panel-7", "panel-8", "panel-9", "panel-10"];
for (var i = 0; i < styledElements.length; i++) {
var target = document.getElementById(styledElements[i]);
console.log(target);
if (target) {
bindEvent(target);
}
}
};
function bindEvent(target) {
console.log(target);
target.bindEvent("ngClick", selectPanel);
};
function selectPanel(event) {
var path = angular.element(event.target);
panels = []; // Reset
if (attrs.testKitDesigner && attrs.testKitDesigner === 'advanced') {
panels.push(path);
} else {
var parent = path.parent();
var paths = parent.children();
for (var i = 0; i < paths.length; i++) {
var current = angular.element(paths[i]);
var tag = current[0].nodeName;
if (tag === 'path' || tag === 'polyline') {
panels.push(current);
}
}
console.log(panels.length);
}
};
return {
restrict: 'A',
templateUrl: 'Views/Templates/designer.html',
link: function (scope, element, attrs) {
scope.step = 0;
scope.sport = 'General';
scope.garment = 'Dress';
scope.design = 'Angelus';
scope.nextStep = function () {
scope.step++;
};
scope.setSport = function (sport) {
scope.sport = sport;
scope.setSvg();
scope.nextStep();
};
scope.setGarment = function (garment) {
scope.garment = garment;
scope.setSvg();
scope.nextStep();
};
scope.setDesign = function (design) {
scope.design = design;
scope.setSvg();
scope.nextStep();
};
scope.setSvg = function () {
var children = element.children();
var template = scope.sport + '/' + scope.garment + '/' + scope.design;
for (var i = 0; i < children.length; i++) {
var child = angular.element(children[0]);
if (child.hasClass('base')) {
child.attr('test-svg', template);
bindEvents();
return;
}
}
}
scope.setColor = function (color) {
for (var i = 0; i < panels.length; i++) {
var panel = angular.element(panels[i]);
var parent = panel.parent();
if (parent.attr('id') === 'piping') {
panel.css({
'stroke': color
});
} else {
panel.css({
'fill': color
});
}
}
};
scope.init = function () {
bindEvents();
};
scope.init(); // Set our defaults;
}
}
})
.directive('testSvg', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.contentUrl = 'Views/Templates/' + attrs.testSvg + '.svg';
attrs.$observe('testSvg', function () {
console.log(attrs.testSvg);
scope.contentUrl = 'Views/Templates/' + attrs.testSvg + '.svg';
});
},
template: '<div ng-include="contentUrl"></div>'
};
});
And the designer template looks like this:
<div class="base" test-svg="/General/Polo/Angelus">
</div>
<div class="options">
<h1>Simple kit designer</h1>
<div ng-hide="step != 0">
<p>Choose your sport.</p>
<ul class="list-unstyled">
<li><a href ng-click="setSport('Netball');">Netball</a></li>
<li><a href ng-click="setSport('General');">General</a></li>
</ul>
</div>
<div ng-hide="step != 1">
<p>Choose your garment.</p>
<ul class="list-unstyled">
<li><a href ng-click="setGarment('Dress');">Dress</a></li>
<li><a href ng-click="setGarment('Polo');">Polo</a></li>
</ul>
</div>
<div ng-hide="step != 2">
<p>Choose your design.</p>
<ul class="list-unstyled">
<li><a href ng-click="setDesign('Angelus');">Angelus</a></li>
</ul>
</div>
<div class="colors" ng-hide="step != 3">
<p>Click an area to change the colour.</p>
<ul id="colour-picker" ng-hide="!picking" class="colours">
<li><a class="colour-red" href ng-click="setColor('red');"></a></li>
<li><a class="colour-orange" href ng-click="setColor('orange');"></a></li>
<li><a class="colour-yellow" href ng-click="setColor('yellow');"></a></li>
<li><a class="colour-green" href ng-click="setColor('green');"></a></li>
<li><a class="colour-blue" href ng-click="setColor('blue');"></a></li>
<li><a class="colour-indigo" href ng-click="setColor('indigo');"></a></li>
<li><a class="colour-violet" href ng-click="setColor('violet');"></a></li>
</ul>
</div>
</div>
Now, what should happen, is that when the user selects a sport or garment or design, the test-svg attribute should change to the new values and then the relevant svg will be loaded.
The attribute does change, but the observe function never gets called. I am certain that this is something to do with the scope but I can't figure it out.
You are adding test-svg attribute during link phase of test-kit-designer. The test-svg attribute are not compiled as directive so the $observe is not triggered, read up on $compile to solve your problem.
However, I would recommend restructuring your code. Consider using test-svg in the template, exposing template from setSvg in test-kit-designer and two-way binding it to another variable in test-svg.

Jquery - Auto Increment ID's for Nested List

I have a nested list that resembles this:
<div id="treeview-left">
<ul>
<li>Country
<ul>
<li>Region
<ul>
<li>District
<ul>
<li>Group
<ul>
<li>People</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
This list is dynamically generated, and I need to auto increment ID's for each list item at each level.
Eg.
Country li's would have #Lv1-1 , #Lv1-2, #Lv1-3
Region li's would have #Lv2-1 , #Lv2-2, #Lv2-3
Each level needs to start with at 0 or 1, and increment the id based on it's index in that specific ul.
This is my current code, I am unable to even get the first level working.
<script>
$(function () {
$("#treeview-left > ul li").each(function () {
var TopPosition = $(this).index();
console.log(TopPosition);
$(this).id("Lvl1"+TopPosition);
});
});
</script>
Your help is appreciated.
Thanks
demo: https://so.lucafilosofi.com/jquery-auto-increment-ids-for-nested-list/
$(function () {
$("#treeview-left ul").each(function (i, item) {
var Tp = i + 1;
$(this).find('li').each(function (j, item) {
$(this).attr('id', "Lvl" + Tp + '-' + (j + 1));
});
});
});
here is a Recursive solution
function updateIds($node, index) {
if ($node.is('ul')) {
updateIds($node.children(), index);
} else {
$node.each(function (i, el) {
$(el).attr('id', 'Lv' + index + '-' + (i+1));
var $child = $node.children('ul');
if ($child.length > 0) {
updateIds($child, index+1);
}
});
}
}
demo: http://jsfiddle.net/3xpBw/1/
Another solution could be:Fiddle
<script type="text/javascript">
$(function () {
var count=0
$('ul').each(function(i, ul) {
count++
ul = $(ul);
var first=count
ul.children('li').each(function(i, li) {
li = $(li);
var second = first + '.' + (li.index() + 1);
li.prepend('<span>' + second + '</span>');
li.attr('id',second)
})
})
});
</script>

Categories

Resources