How to order divs with JQUERY - javascript

i have divs like this:
<div class="oferta SYB">
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas">Pagás<br/> $ 67</div>
<div class="ofertavalor">Valor<br /> $ 160</div>
<div class="ofertadescuento">Descuento $ 93</div>
</div>
</div>
i want to order the divs with the class="oferta" by the value in "ofertapagas" or the value in "ofertavalor" or the value in "ofertadescuento", i dont know how to, i cannot use the database, i just can use Jquery, i am using the last version of it.
Some Help please!!

jQuery abstracts Array.prototype.sort. Since jQuery wrappet sets are Array like objects, you can just call .sort() on them or apply sort on them.
var $datosoferta = $('.datosoferta');
$datosoferta.children().detach().sort(function(a,b) {
return +a.textContent.split(/\$/)[1].trim() - +b.textContent.split(/\$/)[1].trim();
}).appendTo($datosoferta);
See http://typeofnan.blogspot.com/2011/02/did-you-know.html
Demo: http://jsfiddle.net/Rfsfs/

Using ui. JQuery UI - Sortable Demos & Documentation
For your code;
$( ".offer" ).sortable({
update: function(event, ui) { // do post server. }
});

If you place those offer DIVs in some sort of container (another DIV?) you can then fetch all the offers and sort by their childrens' values.
In the HTML below, I put the offer divs inside a container div, and added a data-value attribute to each of the child divs containing data, for easier access (no regular expressions required).
<div id="ofertas_container">
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="67">Pagá $ 67</div>
<div class="ofertavalor" data-value="130">Valor $ 130</div>
<div class="ofertadescuento" data-value="93">Descuento $ 93</div>
</div>
</div>
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="57">Pagá $ 57</div>
<div class="ofertavalor" data-value="150">Valor $ 150</div>
<div class="ofertadescuento" data-value="43">Descuento $ 43</div>
</div>
</div>
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="107">Pagá $ 107</div>
<div class="ofertavalor" data-value="250">Valor $ 250</div>
<div class="ofertadescuento" data-value="1000">Descuento $ 1000</div>
</div>
</div>
</div>
Pagá
Valor
Descuento
The JS / jQuery function:
ofertas_sort = function(sort_key) {
// array of offer divs
var ofertas = $('.infoferta');
// the div classname corresponding to the key by which
// we are sorting
var sort_key_sel = 'div.' + sort_key;
// in large lists it'd be more efficient to calculate
// this data pre-sort, but for this instance it's fine
ofertas.sort(function(a, b) {
return parseInt($(sort_key_sel, a).attr('data-value')) -
parseInt($(sort_key_sel, b).attr('data-value'));
});
// re-fill the container with the newly-sorted divs
$('#ofertas_container').empty().append(ofertas);
};
Here's the code on jsFiddle, with some links at the bottom of the HTML to demo the sorting: http://jsfiddle.net/hans/Rfsfs/2/
I changed some of the values to make the sorting more visible.

Related

JS and querySelectorAll

A querySelectorAll question, most likely a silly one, but I don't see the solution.
I have something like the following
<div id="main_0"> ... </div>
<div id="main_1"> ... </div>
<div id="main_1_minor"> ... </div>
<div id="main_2"> ... </div>
<div id="main_2_minor"> ... </div>
.
.
I wish to select all and only those div's without minor.
I tried
var pattern = new RegExp('^main_\\d');
var elSelected = document.querySelectorAll('div[id^=main_]');
elSelected.filter(elt => pattern.test(elt.id)));
but clearly it is not enough. I am not sure how to formulate by RegEx that the id value has to terminate with a digit. I tried something like RegExp('^main_\\d$'); but I did not get it right.
You can use the :not() selector with the "attribute ends with" selector.
"div:not([id$=minor])"
If it should also verify that the id starts with main_, then you can add that too as you show in your question.
"div[id^=main_]:not([id$=minor])"
So this says "select all div elements where the id starts with main_ and does not end with minor".
If minor is not necessarily at the end, then you can use id*=minor for "contains" instead.
document.querySelectorAll("div[id^=main_]:not([id$=minor])")
.forEach(el => el.style.color = "red");
<div id="main_0"> main </div>
<div id="main_1"> main </div>
<div id="main_1_minor"> main ends with minor </div>
<div id="main_2"> main </div>
<div id="main_2_minor"> main ends with minor </div>
The filter won't work for NodeList, cast to array first. Also if you already selected all main divs the simplest regex would be enough.
var pattern = new RegExp(/\d+$/);
var elSelected = Array.prototype.slice.call(document.querySelectorAll('div[id^=main_]'));
elSelected.filter(elt => pattern.test(elt.id)).forEach(function(elt){
elt.style.backgroundColor = "yellow";
});
<div id="main_0">main_0</div>
<div id="main_1">main_1</div>
<div id="main_1_minor">main_1_minor</div>
<div id="main_2">main_2</div>
<div id="main_0_minor">main_0_minor</div>

Container structure issue with JQuery - Containing containers

I'm finishing up a memory game for school and I'd really like the cards to flip with a CSS animation, which on it's own is pretty straight forward. However I'm pretty new to JavaScript and JQuery which is leading to some trouble with achieving the proper container structure I need to make the cards flip when they are clicked.
Presently the game pieces generate within the board as follows:
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard")
.append($("<div>").addClass("front")//
.append($("<div>").addClass("back").append($("
<img>").attr("src", cards[i]))));
});
};
OR:
<div class="gameBoard>
<div class="front"></div>
<div class="back"><img src="cards"></div>
</div>
But in order for the animation to function properly both the front and back divs need to exist in the same container like this:
<div class="gameBoard>
<div class="flip">
<div class="front></div>
<div class="back"><img src="cards></div>
</div>
</div>
How can I add the div I need (.flip) but have it contain the front and back divs, not just append on to the other divs being generated within the .gameboard container.
Thanks.
It's much simpler to create your DOM using template literals rather than jQuery methods. That way you just describe the HTML as you're accustomed to.
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard").append(`
<div class=flip>
<div class=front></div>
<div class=back><img src="${cards[i]}"</div>
</div>
`);
});
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<div class=gameBoard></div>
You'll notice the ${cards[i]}, which lets you perform string interpolation by executing at runtime the code in the braces.
Here's a vanilla JS version.
const generate=(cards)=>{
var gb = document.querySelector(".gameBoard");
cards.forEach(card =>
gb.insertAdjacentHTML("beforeend", `
<div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>
`)
);
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>
It also uses card instead of cards[i], and an arrow function for the callback.
And this one performs a single append.
const generate=(cards)=>{
document.querySelector(".gameBoard")
.insertAdjacentHTML("beforeend", cards.map(card =>
` <div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>`).join(""));
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>

Jquery Text Swap - Multiple Use Issue

So I used a script that I found on Stack Overlow to swap text. It worked great initially but then I tried to use it again on the same page and I noticed an issue.
You can see the problem here: JsFiddle
The HTML
<div class="gallerycard">
<div id="textMessage"></div>
<div class="textContent">
<div class="girlname">ONE LEFT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO LEFT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
<div class="gallerycard">
<div id="textMessage"></div>
<div class="textContent">
<div class="girlname">ONE RIGHT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO RIGHT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
The Jquery
var cnt=0, texts=[];
// save the texts in an array for re-use
$(".textContent").each(function() {
texts[cnt++]=$(this).text();
});
function slide() {
if (cnt>=texts.length) cnt=0;
$('#textMessage').html(texts[cnt++]);
$('#textMessage')
.fadeIn('fast').animate({opacity: 1.0}, 800).fadeOut('fast',
function() {
return slide()
}
);
}
slide()
So, how do I keep them from merging?
You need two arrays, one for each,
give each one of the gallerycards different ids
and do it twice
var cnt=0, firstTexts=[], secondTexts=[];
// save the texts in an array for re-use
$('#firstID > .textContent').each(function() {
firstTexts[cnt++]=$(this).text();
});
cnt = 0;
// save the texts in an array for re-use
$('#secondID > .textContent').each(function() {
secondTexts[cnt++]=$(this).text();
});
and call slide twice with the relevant array and id
There are multiple problems based entirely on too much copy/paste without understanding the why.
Both target divs have the same id. You should never have two elements on the same page which share the same id. Now there is a quick and dirty way to clean this up and there is a flexible and effective way to clean this up. I went for the flexible solution and I'll explain how it works as best I can.
<div class="gallerycard" data-target="textMessageLeft">
<div id="textMessageLeft"></div>
<div class="textContent">
<div class="girlname">ONE LEFT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO LEFT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
<div class="gallerycard" data-target="textMessageRight">
<div id="textMessageRight"></div>
<div class="textContent">
<div class="girlname">ONE RIGHT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO RIGHT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
Notice I added a data-target element to the gallerycard containing the id of the div we want to place the text into. I also changed the ids on each target div to be unique. This is critical to make it all work, as is the data-target element matching those ids.
texts = {};
// save the texts in an array for re-use
$(".textContent").each(function () {
var target = $(this).parent().attr('data-target');
if (texts[target] == null) { texts[target] = []; }
texts[target].push($(this).text());
});
function slide(divId, cnt) {
if (cnt >= texts[divId].length) cnt = 0;
$('#' + divId).html(texts[divId][cnt++]);
$('#' + divId)
.fadeIn('fast').animate({
opacity: 1.0
}, 800).fadeOut('fast',
function () {
return slide(divId,cnt)
});
}
for (var t in texts)
{
slide(t, 0);
}
In the javascript I changed a lot to make this an expandable and flexible solution, rather than simply duplicating what was already there with two separate names.
First, we removed the counter and changed texts to an object ({} instead of []). From here I can use texts like a hash, which simplifies the rest of the script. The key of the hash is the value of the data-target from the container div of the message and content divs. Add as many content divs as you want under each parent and they'll all be found and associated correctly.
The texts from each textContent div are stored in an array, but we are using the push() function to eliminate the need for a counter variable - counters are fine for a single instance, but they get ugly with multiples.
I changed the slide function to accept two variables: divId and cnt. divId is how the slider knows which div to target and cnt allows the recursive call to keep a private counter which will not conflict with other instances of the slider function running simultaneously.
Finally, to again prevent duplication and allow further expansion, Instead of simply calling slide, we iterate through the hash to get the divId and call a slide instance for each divId we have. Go ahead and try expanding the number of panes or adding new textContent divs under one of the headers. It all works very smoothly now.
The fiddle is here: http://jsfiddle.net/AX4LC/4/

Move around dynamic divs in blog post listings

I am working with a blog and want to move the category above the title on each post listing. when I use the following code I get 50 clones of the first category. I think I need to be using the index parameter of .each() but I'm not sure. Here's my code:
jQuery(document).ready(function(){
jQuery(".blog-category").each(function(){
jQuery(this).insertBefore( jQuery(".blog-head") ) ;
});
});
Essentially I'm trying to insert the
.blog-category
before the
.blog-head
on each post.
HTML
<div id="entry-2839">
<div class="blog-post-in">
<div class="blog-head">content</div>
<div class="blog-side">
<div class="blog-category">more content</div>
</div>
</div>
</div>
This should do the trick:
var e = jQuery(this);
e.closest(".blog-post-in").prepend(e);

Way to flip sort order of items in div tag?

I have a list of notes that are being posted to a view from a model and being posted to the page
<div id="listResults"></div>
<div class="pam bgpage line" id="navResults"></div>
<script id="listTemplate" type="text/x-jquery-template">
<div class="clearfix ptl phl modalRow">
<div class="ui ui_std_${Type} activity_icon"></div>
<div class="activity">
<p class="activity_head">${SubType} ${Type}</p>
<span class="activity_detail">${CreatedDuration} by ${CreatedBy}</span><br />
<div class="activity_wrap">
{{html Note}}
</div>
</div>
</div>
</script>
<script id="navTemplate" type="text/x-jquery-template">
<div class="unit size1of5 lastUnit">
{{if TotalItemCount>0}}
<label class="dgray mlm mrs">1 - ${LastItemOnPage} of ${TotalItemCount}</label>
{{else}}No Recent Activity{{/if}}
</div>
</script>
These notes are being posted in a descending order i want to make a javascript or jquery function that would take a button onclick and flip the order to ascending. Is there a way to do taht easily? I cannot find an answer.
You could try something like this, need to add the sort value to the div containing the data you would like to sort and wrap all the articles in a container div:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script type="text/javascript" src="jquery-1.10.1.js"></script>
</head>
<body>
<input type="button" id="test" value="sort"/>
<div id="activitycontent">
<div class="activity" data-sort="22">
<span>22</span>
</div>
<div class="activity" data-sort="11">
<span>11</span>
</div>
<div class="activity" data-sort="33">
<span>33</span>
</div>
</div>
<script type="text/javascript">
(function () {
var direction = 1;
function sortHelper(a, b) {
// this can be made faster if you pre store these values
// retuns 1,-1 or 0 to sort the array of div elements
// using the value of the attribute data-sort
vala = a.getAttribute("data-sort");
valb = b.getAttribute("data-sort");
ret = vala>valb?1:(vala<valb)?-1:0;
// multiply by direction (ascending descending)
return ret * direction;
}
$("#test").on("click", null, null, function () {
// sort opposite of previously used direction.
direction = direction * -1;
// get array of html Div elements containing the article
var divArr = Array.prototype.slice.call
($("#activitycontent div.activity"), 0);
var i = 0;
var container = document.getElementById("activitycontent");
// sort the articles based on data-sort property
divArr.sort(function (a, b) {
return sortHelper(a, b);
});
// add the div elements containing the articles in the right order
for (i = divArr.length-1;i>-1;i--){
container.appendChild(divArr[i]);
}
});
})();
</script>
</body>
</html>
Well, if you had them posted in a table rather than in divs, and the thing you were sorting on was one of the columns, the jquery tablesorter plugin would hook you right up. It's a good one to learn anyway.
Alternate option - if you have enough control over your model, you could post them twice, once in the initial order, once in reverse order. Have the reverse order version start out hidden, and then when you press the button, use jQuery's .show() and .hide() to swap which is visible.
Alternate option - if you don't have that level of control over your model, you can use jquery to iterate through the list of divs once the apge is loaded, grab the list of notes with a selector, and then use .each(), .clone() and .insertBefore() to populate your reversed list, and then use the show/hide button as before.
I hope you find this helpful.

Categories