jQuery Not Rearranging Elements on my Django Page - javascript

I'm trying to add 2 sort options to a web page. Currently, when I click the respective links (.alpha and .finish), I see an animation on the screen and can tell that the names are moving, but then the output remains the same. Below is the jQuery script I'm working with off of this JSFiddle that I found in a google search.
var $divs = $('div.col-xs-3');
$('.alpha').on('click', function () {
var alphabeticallyOrderedDivs = $divs.sort(function (a, b) {
return $(a).attr('data-name') > $(b).attr('data-name');
});
$('.people').html(alphabeticallyOrderedDivs);
});
$('.finish').on('click', function () {
var numericallyOrderedDivs = $divs.sort(function (a, b) {
return $(a).attr('id') < $(b).attr('id');
});
$('.people').html(numericallyOrderedDivs);
});
I am using Django to display the page and currently have an order_by to correctly order each div alphabetically. Is this affecting it?

Three things:
jQuery doesn't officially have a sort function, so don't rely on it.
The sort function it does have (unofficially) it gets from Array.prototype. We'll use that one directly. The way that sort function's callback works, it's expected to return a number, not a boolean: A negative number if the first argument should appear before the second, 0 if they're equal for sorting purposes, and greater than zero if the second should appear before the first.
Officially, html doesn't accept either a jQuery object or an array (just strings and functions), so let's use empty().append(...) instead:
So to fix the code in your question, first we use get to get a true array from the jQuery set, and then we use localeCompare to correctly compare the names:
var $divs = $('div.col-xs-3');
$('.alpha').on('click', function () {
var alphabeticallyOrderedDivs = $divs.get().sort(function (a, b) {
// Note -----------------------------^^^^^^
return $(a).attr('data-name').localeCompare($(b).attr('data-name'));
// Note -------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
});
$('.people').empty().append(alphabeticallyOrderedDivs);
// Note -----^^^^^^^^^^^^^^
});
<input type="button" class="alpha" value="Alpha">
<div class="people">
<div class="col-xs-3" data-name="q">q</div>
<div class="col-xs-3" data-name="p">p</div>
<div class="col-xs-3" data-name="j">j</div>
<div class="col-xs-3" data-name="e">e</div>
<div class="col-xs-3" data-name="t">t</div>
<div class="col-xs-3" data-name="d">e</div>
<div class="col-xs-3" data-name="a">a</div>
<div class="col-xs-3" data-name="q">q</div>
<div class="col-xs-3" data-name="r">r</div>
<div class="col-xs-3" data-name="x">x</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

Reload Data with .sort from Dynamic Table with Jquery | JSON | Sort with New Data

I have been trying for a few days that when I use the .sort property the data is eliminated or modified instead of it being reloaded as new lines.
Attach Captures from the code Working
Image1 How work the code when i press the button, this sort to the highest price to lowest but how do you can see in the second image, the code appears up and this not delete the old data
Marked with "X" the data that does not have to show
this fragment is the one that generates the tables dynamically
const mostrarProductos = () => {
$.getJSON(URLJSON, (respuesta) => {
for (let z of respuesta) {
productosv2.push(z);
}
for (let x of productosv2) {
$("#fila").append(`
<tr class="deleteProductos">
<div class="card text-center" style="width: 18rem;" id='btnBorrarCarrito'>
<div class="card-body">
<input type="hidden" id="idProd" value="${x.id}"> </td>
<td class="card-title" id="${x.id}">${x.producto}</h2> </td>
<td class="card-text">$ ${x.precio}</p></td>
<div class="btn-group" role="group" aria-label="Basic mixed styles example">
<td><button type="button" class="btn btn-success" onclick="agregarCarrito(${x.id})">Agregar</button></td>
</div>
</div>
</div>
</tr>
`);
}
$("#fila").fadeIn("5000");
});
};
And this function is what orders them
function respuestaClickExpensive() {
$("#fila").html('');
let productosordenados = productosv2.sort((a, b) => {
if (a.precio > b.precio) {
return -1;
}
if (a.precio < b.precio) {
return 1;
}
return 0;
});
return productosordenados;
}
The one that orders them from smallest to largest is the same only that different signs and names.
As you can see I tried to use a ".html ("")" since previously in another cart attempt it used .innerHtml (Which does not work in this case either, Also the code of the cart is totally different from that moment that it worked for me
)
I tried the following:
$ ("#fila"). empty ();
Make another function to clean with .empty
Use Native JavaScript to generate the code.
$ ("#fila"). remove (); this removes all the content for me but does not regenerate it.
Change the HTML tag "Row" to a previous div which, since the div was not generated again, did not generate it again.
$ ("#fila tr"). remove ();
And some more things that I don't remember right now.
If you can guide me on what I did wrong or any suggestions to fix it, I appreciate it.
If I had to follow a template about posting on StackOverFlow or having chosen or named in a different way, I appreciate your comment since it is my first post
Project notes of possible relevance: The complete code outside of html and css is made with Native JavaScript, Jquery, Ajax, SASS and BootStrap.

jQuery .size() function doesn´t work with a variable

At many points in my code I need to know how many .page classes are used.
To know this I use $(".page").size()
I want to save this information into a variable.
So I wrote this:
var vari = {
*more variables*
totalPageCount : $(".page").size()
};
The Problem is that vari.totalPageCount always gives 0 back.
With console.log() I get this:
console.log($(".page").size()); // Return 8
console.log(vari.totalPageCount); // Return 0
Edit:
Here is a example how i use it.
JS:
var vari = {
currentPage : 0,
pageAnimations : 0,
animationList : ".fade-in, .fade-out",
totalPageCount : $(".page").size(),
};
var footer = {
html : function(){
var html;
var date = this.date();
for(var i=0; i<vari.totalPageCount; i++){
html = '<span class="pageNumber" id="pageNumber">Folie:'+i+' • '+vari.custom["companyName"]+' • '+date+'</span>';
$("#normalPage"+i).append(html);
}
return;
}
};
HTML:
<body class="presWrapper">
<div class="pageWrapper">
<div class="page startPage" id="startPage">
<h2 class="mainTitle">Lorem</h2>
<h4 class="subTitle">Ipsum</h4>
</div>
</div>
<div class="pageWrapper">
<div class="page normalPage" id="normalPage1">
<div class="content">
<p class="fade-in">HELLO WORLD</p>
</div>
</div>
</div>
<div class="pageWrapper">
<div class="page endPage" id="endPage">
<div class="content">
<p class="fade-out">HELLO SATURN</p>
<p class="fade-out">HELLO WORLD</p>
<p class="fade-in">HELLO WORLD</p>
<p>pTag</p>
</div>
</div>
</div>
</body>
Any suggestions to solve this problem?
vari.totalPageCount Gets evaluated only when it is declared.
As a result it will only have the value of $(".page").size() when it is first run.
Unless you are waiting on document ready the children of .page have not yet been added and it has length 0.
When you later call the console and execute the selector again - you get the true count in the console message - but the stored value has already been calculated as 0 within vari.
length() and size() are equivalent functions in jquery but size has been deprecated so length is the appropriate function to call. But in either case - its likely you are just evaluating the length too early when the vari object is constructed for it to have a meaningful value.
Does the following give you the wrong value for the property:
$(document).ready(function () {
var vari = {totalPageCount: $('.page').length};
console.log(vari.totalPageCount);
});
Relevant documentation
The .size() method is deprecated as of jQuery 1.8.
Use length property instead:
var vari = {
*more variables*
totalPageCount : $(".page").length;
};
Also, make sure you are using this code at the bottom of the script or inside a document ready handler. You won't get accurate information if you try to get it before DOM has been fully setup.
This will increment count for every element with having the page class
var count = 0;
$('.page').each(function() {
count++;
});
var vari = {
totalPageCount: count
};
Working jsFiddle

too much HTML in an ajax script?

I read from this page that appending a lot of elements is bad practice and I should build up a string during each iteration of the loop and then set the HTML of the DOM element to that string. Does the same go for using too much HTML in the loop?
I have an AJAX script that parses JSON data. It requires adding data to different existing elements, like this:
$.ajax({
url: "url",
success: function (data) {
$(data.query.results.json.json).each(function (index, item) {
var title = item.title, // A,B,C or D
age = item.age,
background = item.background,
ingredient = item.Ingredient;
$('.'+ title+'_ingredient').html(''+ingredient+'')
$('.'+ title+'_age').html(''+age+'')
$('.'+ title+'_background').html(''+background+'')
});
},
error: function () {}
});
HTML:
<div class="A_ingredient"></div>
<div class="B_ingredient"></div>
<div class="C_ingredient"></div>
<div class="D_ingredient"></div>
<div class="A_age"></div>
<div class="B_age"></div>
<div class="C_age"></div>
<div class="D_age"></div>
<div class="A_background"></div>
<div class="B_background"></div>
<div class="C_background"></div>
<div class="D_background"></div>
Is it necessary to build up a string first? If so, can you show me how to do that?
It is purely about the time it takes to process calls to html() so they simply recommend you reduce the number of calls. In this case you could build them once in a loop then sets the div html once for each.
Update:
Based on your update, aside from all the extra trailing quotes you don't need to add (a string is a string is a string), your code is fine as is. You only hit each item once.
e.g.
$.ajax({
url: "url",
success: function (data) {
$(data.query.results.json.json).each(function (index, item) {
var title = item.title, // A,B,C or D
age = item.age,
background = item.background,
ingredient = item.Ingredient;
$('.'+ title+'_ingredient').html(ingredient);
$('.'+ title+'_age').html(age);
$('.'+ title+'_background').html(background);
});
},
error: function () {}
});
Note: If your item properties (Age, Background, Ingredient) are simple values (not objects or arrays), yo do not need the leading ''+s either.
Previous
Assuming you actually want to concatenate the results (you are only keeping the last ingredient at the moment), you could do something like this:
e.g.
$.ajax({
url: "url",
success: function (data) {
var ingredients = '';
$(data.query.results.json.json).each(function (index, item) {
var title = item.title;
var ingredient = item.Ingredient;
ingredients += ingredient;
});
$('.aclass').html(ingredients);
$('.bclass').html(ingredients);
$('.cclass').html(ingredients);
$('.dclass').html(ingredients);
},
error: function () {}
});
Which can be reduced to:
$('.aclass,.bclass,.cclass,.dclass').html(ingredients);
The contents of each div are identical in your example, so you only need a single string.
In this instance you would probably need some form of delimiter between ingredients, but your example is too vague.
e.g.
ingredients += ingredient + '<br/>';
In your example, you're setting the HTML on many different document elements.
If they're grouped in some way, for example all in a Div with ID #Container, you could build a string of the HTML and set the content of the whole Div at the end of it, something like this:
$.ajax({
url: "url",
success: function (data) {
var sHTML="";
$(data.query.results.json.json).each(function (index, item) {
var title = item.title,
background = item.background,
ingredient = item.Ingredient;
// not sure what your actual HTML is (div/span/td etc) but somethign like this?
sHTML+="<div>"; // an opening container for this item
sHTML+='<div class="'+title+'_ingredient">'+ingredient+'</div>')
sHTML+='<div class="'+title+'_age">'+title+'</div>')
sHTML+='<div class="'+title+'_background">'+background+'</div>')
sHTML+="</div>";
});
$("#Container").html(sHTML);
},
error: function () {}
});
Note I haven't tested this code, but you see the principal hopefully.
That is, build a string of the HTML then set one element at the end with the content.
I have done this a lot in a recent project and haven't seen any speed issues (maybe 50 'items' to set in my case).
HTML will initially look like this :
<div id="container">
</div>
Then end up like this (2 x items in this example) :
<div id="container">
<div>
<div class="<sometitle1>_ingredient">ingredient 1</div>
<div class="<sometitle1>_age">age 1</div>
<div class="<sometitle1>_background">background 1</div>
</div>
<div>
<div class="<sometitle2>_ingredient">ingredient 2</div>
<div class="<sometitle2>_age">age 2</div>
<div class="<sometitle2>_background">background 2</div>
</div>
</div>
subsequent calls will replace the element's content with new values, replacing the old items.
Building a string is, I would imagine, less processor-heavy than setting the html() on lots of elements individually. Each time you use html() I'm guessing that the browser has to go some way towards working out any knock-on effects like expanding the width of an element to accomodate it or whether events will still work as they did, etc - even if actual rendering is only run at the end of the process. This way you use html() once, although what you're setting is more complex.
Hope this helps.

knockout apply foreach binding using script

I trying to apply binding using script as below code:
<div data-bind="custom: {}">
<div class="list">
<span data-bind="text: text, click: doSomething"></span> >
</div>
</div>
ko.bindingHandlers.custom = {
init: function (ele) {
var list = ko.observableArray([]),
ee = $(ele).find('.list')[0]
... code / event / callback / trigger to keep the list data change
ko.bindingHandlers.foreach.apply(this, ee, list) //this line not work
}
}
which similar like :
<div data-bind="custom: {}"> //tested and confirm work
<div class="list" data-bind="foreach: ko.observableArray([ { id: 'a', text: 'aa' } ])">
<span data-bind="text: text, click: doSomething"></span> >
</div>
</div>
my question is what wrong with this
ko.bindingHandlers.foreach.apply(this, ee, list)
?
try at fiddle
You need to use call rather than apply; call takes a context and any number of arguments which are passed to the function, whereas apply takes a context and a single array argument, whose elements are passed as arguments to the function.

jQuery javascript custom sort procedure works in Firefox, but IE doesn't seem to get it... (copy-paste example code)

i've built this sample code based on a real problem I have in an application. I've got a custom sort procedure to sort jQuery arrays. A container contains a list of items with special attributes.
For sorting:
Loads all items in temp array
Clears the container
Sorts the temp array into a new array
Append sorted items to container
Somehow Firefox knows how to sort but IE doesn't. Can somebody tell me what isn't working properly?
(you can copy-paste the html below into a empty .html file, it should work immediately)
<html>
<head>
<script type="text/javascript"
src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
jQuery.fn.sort = function() {
return this.pushStack( [].sort.apply( this, arguments ), []);
}
function DoTheSort() {
//Fetch elements in jQueryElement
var sortableArray = $('#sortables').find('div.sortable');
//Clear the sortables container
$('#sortables').empty();
//Sort the array
var sortedArray = $(sortableArray).sort(sortProcedure);
//Append sorted items
jQuery.each(sortedArray, function() {
alert($(this).attr("sortvalue"));
$('#sortables').append(this);
});
}
function sortProcedure(a, b) {
var value1 = parseInt($(a).attr("sortvalue"));
var value2 = parseInt($(b).attr("sortvalue"));
return value1 > value2;
}
</script>
</head>
<body>
Sort
<div id="sortables">
<div class="sortable" sortvalue="5">5</div>
<div class="sortable" sortvalue="1">1</div>
<div class="sortable" sortvalue="4">4</div>
<div class="sortable" sortvalue="1">1</div>
<div class="sortable" sortvalue="2">2</div>
<div class="sortable" sortvalue="9">9</div>
<div class="sortable" sortvalue="3">3</div>
</div>
</body>
</html>
Your sort procedure is subtly wrong: you need to account for equalities as well, and boolean is not the correct return type (see addendum).
Do this:
return value1 - value2;
instead of:
return value1 > value2;
Addendum:
The general form of a sort comparison function f(A,B) needs to return > 0 if A > B, < 0 if A < B, and 0 if no alteration needs to occur. Returning a boolean gets you caught by falsey values not representing what you think they do.

Categories