This is the HTML elements which i am trying to sort
<div id="notStarted-tasks">
<div id="K1512844566066" class="row">
<div class="span4 offset4 alert alert-warning" style="margin: auto; display: table;"><span style="font-size: 170%">Breakfast</span>
<button href="#" type="button" class="btn btn-warning">Start</button>
</div>
</div>
<div id="I1512844569230" class="row">
<div class="span4 offset4 alert alert-warning" style="margin: auto; display: table;"><span style="font-size: 170%">dinner</span>
<button href="#" type="button" class="btn btn-warning">Start</button>
</div>
</div>
</div>
And this is my sort function in javascript, in the first "if" block i am just returning if the list is empty. In the second "if" i am doing a descending sort and in the "else" ascending sort.
const todoSort = () =>{
if(notStartedElement.length==0)
return;
if(todoCurrent=="asc"){
document.getElementById("todoSort").setAttribute("src","src/image/desc.png");
todoCurrent="desc";
const ele = document.getElementById("notStarted-tasks");
const divEle = ele.getElementsByClassName("row");
divEle.sort(function(a,b){
let spana = a.getElementsByTagName("span")[0];
let spanb = b.getElementsByTagName("span")[0];
return spana.innerHTML == spanb.innerHTML? 0 : (spana.innerHTML > spanb.innerHTML ? -1 : 1);
});
ele.innerHTML="";
for(let i = 0;i<divEle.length;i++)
ele.appendChild(divEle[i]);
}
else{
document.getElementById("todoSort").setAttribute("src","src/image/asc.png");
todoCurrent="asc";
const ele = document.getElementById("notStarted-tasks");
const divEle = ele.getElementsByClassName("row");
divEle.sort(function(a,b){
let spana = a.getElementsByTagName("span")[0];
let spanb = b.getElementsByTagName("span")[0];
return spana.innerHTML == spanb.innerHTML? 0 : (spana.innerHTML > spanb.innerHTML ? 1 : -1);
});
ele.innerHTML="";
for(let i = 0;i<divEle.length;i++)
ele.appendChild(divEle[i]);
}
}
When i click on my sort button i get this message "Uncaught TypeError: divEle.sort is not a function". And i need this to be done only in javascript and i am not allowed to use jquery. So, can anybody point me out what am i doing wrong here ?
Thank you
getElementsByClassName returns a HTMLCollection and you need a Array to use sort.
You can easily make it into an array like this:
var collection = document.getElementsByClassName("some-class");
var array = [];
for (var x = 0; x < collection.length; x++) {
array.push(collection[x]);
}
array.sort(...);
Related
This is all my code:
<div class="conteiner">
<h3 class="kitxva hide">Question</h3>
<div class="questionCon " id="questionCon" >
<button class="anwser1 ">Anwser1</button>
<button class="anwser1 ">Anwser1</button>
<button class="anwser1 ">Anwser1</button>
<button class="anwser1 ">Anwser1</button>
<div class="controls">
<button id="start" class="anwser">Start</button>
<button id="next" class="anwser1">Next</button>
</div>
</div>
</div>
const start = document.getElementById('start');
const next = document.getElementById('next');
const questionCon = document.getElementById('questionCon');
const kixtva = document.querySelector('h3')
const anwser = document.querySelectorAll('.anwser1');
let shuffleQuestions, currentQuestionIndex
start.addEventListener('click',startGame);
function startGame(){
for (var i=0;i<anwser.length;i+=1){
anwser[i].style.display = 'block';
}
kixtva.classList.remove('hide');
start.classList.add('hide')
shuffleQuestions = questions.sort(() => Math.random() - .5)
currentQuestionIndex = 0;
setNextQuestion()
}
function setNextQuestion(){
resetState()
showQuestion(shuffleQuestions[currentQuestionIndex])
}
function resetState(){
next.classList.add('hide')
while(anwser.firstChild){
anwser.removeChild(anwser.firstChild)
}
}
There is an error referencing answer.appendChild(button);.
I don't see answer.appendChild(button); in your code but I see that answer is document.querySelectorAll('.anwser1').
Problem is that querySelectorAll() returns all elements as NodeList object - not only one element. You are looking for querySelector() which returns the first element that matches a specified CSS selector or if you want to get all of your answers with querySelectorAll() you have to iterate over each element in NodeList (you can use for or forEach or anything like that).
Im trying to achieve this piece of code but in my console it says thing is null which is weird because when I look in the console, sessionStorage isn't empty...
$(".btn-alert").click(function(){
var identifierOfSpan = $(this > "span").text();
for(var prop in sessionStorage){
var thing = JSON.parse(sessionStorage.getItem(prop))
if(thing.id == identifierOfSpan){
sessionStorage.removeItem(prop);
}
}
$(this).closest(".voyages").remove();
if(sessionStorage.length == 0){
alert("Message!");
location.href="reservation.html"
}
});
the button is supposed to delete the div and the sessionStorage item which looks like this
Html :
<div class="voyages">
<button class="btn btn-alert btn-md mr-2" tabindex="-1">delete the flight</button>
<span>ID : 4224762</span>
<div class="infos">
<img src="img/angleterre.jpg" alt="maroc">
<div>
<ul>
<li><h5>Angleterre, Londres (LON)</h5></li>
<li><h5>2 adulte(s)</h5></li>
<li><h5> Aucun enfants </h5></li>
<li><h5>Type : Couple</h5></li>
</ul>
</div>
</div>
<hr>
<h3>Options</h3>
<ul>
<li>voiture : 0</li>
<li>Hotel : 0 </li>
</ul>
<hr>
<h3>Prix :3713$</h3>
If I'm reading your question correctly, you want to...
Click on a button
Find the first sibling <span> element and parse a number out of its text content
Remove all sessionStorage items (JSON serialized objects) with matching id properties
For the ID, I highly recommend adding some data directly to the <button> to help you identify the right record. If you can, try something like
<button class="btn btn-alert btn-md mr-2" data-voyage="4224762"...
Try something like this
$('.btn-alert').on('click', function() {
const btn = $(this)
const id = btn.data('voyage')
// or, if you cannot add the "data-voyage" attribute
const id = btn.next('span').text().match(/\d+$/)[0]
// for index-based removal, start at the end and work backwards
for (let i = sessionStorage.length -1; i >= 0; i--) {
let key = sessionStorage.key(i)
let thing = JSON.parse(sessionStorage.getItem(key))
if (thing.id == id) {
sessionStorage.removeItem(key)
}
}
// and the rest of your code
btn.closest(".voyages").remove();
if(sessionStorage.length === 0) {
alert("Message!");
location.href = 'reservation.html'
}
})
The problem with using a for..in loop on sessionStorage is that you not only get any item keys added but also
length
key
getItem
setItem
removeItem
clear
I have some code that retrieves users from a server (ajax) and I use some <a> tags to display it, and when you click on an <a> tag with a user, it's supposed to add it to an array in_group. The first one works, the second one goes to the alert() function AND also adds the user to the array, which confuses me. The remove button doesn't work either. What am I doing wrong? I want the user to be added to the in_group array only if doesn't exist, and to be deleted when the button is pressed.
var in_group = [];
$("#students-body").on('click', 'a', function() {
var modal = $("#manageGroupMembers");
var student_id = $(this).attr('student-id');
var student_name = $(this).html();
var student = {
id: student_id,
name: student_name
};
console.log(in_group.length);
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
console.log(in_group[i].id);
if (in_group[i].id === student_id)
{
alert('in grp');
return;
}
else
{
in_group.push(student);
}
}
}
else
{
in_group.push(student);
}
RefreshGroup();
//modal.modal('hide');
});
function RefreshGroup()
{
var students_group = $("#students-group");
var html = "";
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
html += "<span>"+in_group[i].name+"</span>";
html += "<button class='btn btn-danger' onclick='event.preventDefault();RemoveFromGroup("+i+")'>x</button>";
}
students_group.append(html);
}
}
function RemoveFromGroup(index) {
in_group.splice(index, 1);
RefreshGroup();
}
Html:
<div class="form-group">
<label for="group_members">Members</label>
<button class="btn btn-primary" style="display: block;" onclick="event.preventDefault()" id="add-student-btn">Add Member</button>
<div id="students-group"></div>
</div>
Modal:
<!-- Modal -->
<div class="modal fade" id="manageGroupMembers" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Add Member to Group</h4>
</div>
<div class="modal-body">
<div id="students-body"></div>
<div id="pagination-students"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
That's because you are inserting the id in a for loop. Change your code:
...your code before this....
var student_id = $(this).attr('student-id');
var student_name = $(this).html();
var student = {
id: student_id,
name: student_name
};
var index = in_group.findIndex(function(std) {
return std.id == student.id;
});
if(index === -1) {
in_group.push(student);
} else {
alert('student was already in array')
}
This way, you check if the student is inside the array. If not (-1) you insert it. Else you alert
In this section of your code, you’re looping the in_group array to find a student entry with a specific ID.
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
console.log(in_group[i].id);
if (in_group[i].id === student_id)
{
alert('in grp');
return;
}
else
{
in_group.push(student);
}
}
}
else
{
in_group.push(student);
}
RefreshGroup();
If you find it, you alert it, and then you stop the loop and the entire function. If you don’t find it, you push it, but you don’t break the loop. You keep looping, the check in_group[i].id === student_id is re-executed and the alert is executed as well.
You could add a return; after calling RefreshGroup, however, why not get rid of the loops and make things easier?
let isInGroup = in_group.find(studentEntry => studentEntry.id === student_id);
if(isInGroup){
alert("in grp");
}
else{
in_group.push(student);
RefreshGroup();
}
This will replace the above section completely and it will work.
As to why the removal doesn’t work:
event isn’t defined. It’ll likely produce a ReferenceError. (Same problem for your other <button>.)
Instead of inline event attributes, look into event delegation and standard event listeners (jQuery has the click method—use it!)
Look into data- attributes or jQuery’s data to hold the value of the index.
Event delegation could look like this:
var students_group = $("#students-group");
students_group.on("click", "button", function(e){
RemoveFromGroup($(this).attr("data-index"));
});
And then, when generating the HTML:
html += "<button class='btn btn-danger' data-index='" + i + "'>x</button>";
I’d also recommend reading about Array.prototype methods, particularly the iteration methods.
Trying to sort children div based on data attributes
The html code below is being generated by a CM and the data can be retrieved in any random order.
the html code is
<section class="box explore">
<div id="ProductContainer" class="row">
<div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
<h>B</h>
<p>Category_A</p>
</div>
<div id="123" data-name="A" data-category="Category_A" class="explore-cell">
<h>A</h>
<p>Category_A</p>
</div>
<div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
<h>C</h>
<p>Category_A</p>
</div>
<div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
<h>E</h>
<p>Category_B</p>
</div>
<div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
<h>D</h>
<p>Category_B</p>
</div>
</div>
java
$('div').sort(function (a, b) {
var contentA = $(a).attr('data-name');
var contentB = $(b).attr('data-name');
return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
})
Jsfiddle http://jsfiddle.net/w8gkshue/
if someone can point me in the right direct on how to best sort either by Product Name or Category.
Updated hope this gives better explination
EDIT: I missed the jQuery tag... leaving the answer still.
var productCt = document.getElementById('ProductContainer'),
reInsertProductCt = tempRemove(productCt);
[].slice.call(productCt.children)
.sort(function (a, b) {
var aName = a.dataset.name,
bName = b.dataset.name;
return aName < bName? -1 : +(aName > bName);
})
.forEach(productCt.appendChild.bind(productCt));
reInsertProductCt();
function tempRemove(el) {
var parent = el.parentNode,
nextSibling = el.nextSibling;
parent.removeChild(el);
return function () {
if (nextSibling) parent.insertBefore(el, nextSibling);
else parent.appendChild(el);
};
}
<div id="ProductContainer" class="row">
<div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
<h>TEST NAME B</h>
<p>TEST</p>
</div>
<div id="123" data-name="A" data-category="Category_A" class="explore-cell">
<h>TEST NAME A</h>
<p>TEST</p>
</div>
<div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
<h>TEST NAME C</h>
<p>TEST</p>
</div>
<div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
<h>TEST NAME E</h>
<p>TEST</p>
</div>
<div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
<h>TEST NAME D</h>
<p>TEST</p>
</div>
</div>
You can use .sort method like this
var $wrapper = $('#ProductContainer');
$wrapper.find('.explore-cell').sort(function (a, b) {
return a.getAttribute('data-name') > b.getAttribute('data-name');
})
.appendTo( $wrapper );
But I don't sure about the cross browsing support
Calling only sort on them won't actually visually change the DOM, it just returns a sorted collection. So basically you just need to get the collection, sort it, then return it. Something like this should work:
$('#ProductContainer > div').detach().sort(function (a, b) {
var contentA = $(a).data('name');
var contentB = $(b).data('name');
return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
}).appendTo('#ProductContainer');
You'll want to make sure that you use the detach() method and not remove(), as detach() will retain all of the data and events associated with the collection items.
Why choose to sort by category or by name when you can sort by both?
I tried to write a generic multisort function generator, which should also work with the native array sort function.
JSFIDDLE HERE
A function that generates the multisort, it takes two parameters.
The column priority list order (first by category or by name? You decide).
I also wanted a way to provide values for columns (since you might not retrieve them the same way for each of them), it is an object that describes for each column a function to retrieve data.
Here it is
function getMultisortFn(columns, provideColumnData) {
return function (a, b) {
for (var i = 0, l = columns.length; i < l; i++) {
var column = columns[i];
var aColumnData = provideColumnData[column.name](a, column.name);
var bColumnData = provideColumnData[column.name](b, column.name);
if (aColumnData !== bColumnData) {
if (column.asc) {
return String.prototype.localeCompare.call(aColumnData, bColumnData);
}
return String.prototype.localeCompare.call(bColumnData, aColumnData);
}
}
};
}
Now this is the part where you actually use the multisort generated
function retrieveDataAttribute(item, attribute) {
return $(item).data(attribute);
}
var $container = $('#ProductContainer');
var $products = $container.find('div');
var multisort = getMultisortFn([{
name: 'category',
asc: false
}, {
name: 'name',
asc: true
}], {
name: retrieveDataAttribute,
category: retrieveDataAttribute
});
$products.sort(multisort);
And finally the DOM manipulation to apply the new order
$products.detach().appendTo($container);
EDIT thanks to plalx:
$container.detach().append($products).appendTo('section.box.explore');
This question already has answers here:
How to add a class to a given element?
(28 answers)
Closed 9 years ago.
I need to change the class of each href item depending on its value.
I have this code.
<body onload="myFunction()">
<div class="indi-download">
<div class="pull-left">
<h6 class="file" id="file-display-id">%file_display_name%</h6>
</div>
<div class="pull-right">
<a class="download-link" id="download_link" href="%file_url%">Download</a>
</div>
</div>
</body>
In getting the href item on class download-link, I used this javascript code.
function myFunction()
{
var anchors = document.querySelectorAll('a.download-link');
for (var i = 0; i < anchors.length; i++) {
var url = anchors[i].href;
var splitfile = url.split('.').pop();
if(splitfile=='pdf'){
//class="file" would be class="pdf-file"
}else if(splitfile=="docx"){
//class="file" would be class="docx-file"
}else{
//other file format...
}
}
}
on Inspect Element, Something this kind of output.
Element 1 ---
<div class="indi-download">
<div class="pull-left">
//Changed into pdf-file
<h6 class="pdf-file" id="file-display-id">Sample PDF 1</h6>
</div>
<div class="pull-right">
<a class="download-link" id="download_link" href="http://mysite-
info/download/files/file1.pdf">Download</a>
</div>
</div>
Element 2 ---
<div class="indi-download">
<div class="pull-left">
//Changed into docx-file
<h6 class="docx-file" id="file-display-id">Sample docx 1</h6>
</div>
<div class="pull-right">
<a class="download-link" id="download_link" href="http://mysite-
info/download/files/file2.docx">Download</a>
</div>
</div>
How to achieve this kind of output? Changing the classes that depends on the values on href. Any Idea?
If you can use jQuery, I think something like this should work:
function myFunction()
{
var anchors = document.querySelectorAll('a.download-link');
for (var i = 0; i < anchors.length; i++) {
var url = anchors[i].href;
var splitfile = url.split('.').pop();
if(splitfile=='pdf'){
$(anchors[i]).removeClass('file');
$(anchors[i].addClass('pdf-file');
}else if(splitfile=="docx"){
$(anchors[i]).removeClass('file');
$(anchors[i]).addClass('docx-file');
}else{
//other file format...
}
}
}
The class attribute is mapped to the className property so as not to clash with the ECMCAScript future reserved word class, so you want something like:
anchors[i].className = 'docx-file';.
Applied to your example, you can do something like:
var classNames = {pdf:'pdf-file', docx:'docx-file'};
...
anchors[i].className = classNames[splitfile];
and to accommodate a default:
anchors[i].className = classNames[splitfile] || 'default-class';
just in case splitfile doesn't match one of the expected values. And the entire function is:
function myFunction() {
var anchors = document.querySelectorAll('a.download-link');
var classNames = {pdf:'pdf-file', docx:'docx-file'};
for (var i = 0; i < anchors.length; i++) {
var url = anchors[i].href;
var splitfile = url.split('.').pop();
anchors[i].className = classNames[splitfile] || 'default-class';
}
}