I need to do a toggle button...when you click once, it will add money to a total price, if you click the button again and "deselect" it, it will subtract that amount from the total.
I have this HTML that is adding and removing a class to change the background color of the selected button -
<div class="col-md-4" ng-repeat="service in services_data track by $index">
<div ng-class="showDetails[$index] ? 'panel-warning': 'panel-default'" class="panel">
<button ng-click="$parent.showDetails[$index] = !$parent.showDetails[$index]; price(service.price, service.est_time_mins)" class="panel-heading btn"><span class="pull-left badge">$ {{service.price}}</span>{{service.name}}</button>
<div class="panel-body">
{{service.est_time_mins}} mins. {{service.style}}
</div>
</div>
</div>
and this controller -
app.controller('AppointmentController', ['$scope','services', function($scope, services) {
var data = {};
data.fn = 'services';
services.getData(data).success(function(return_data){
console.log(return_data);
$scope.services_data = return_data;
});
var data = {};
data.fn = 'get_barbers';
services.getData(data).success(function(rd){
$scope.barbers = rd;
console.log(rd);
});
$scope.total_price = 0;
$scope.time = 0;
$scope.price = function(price, time){
$scope.total_price = parseInt(price) + parseInt($scope.total_price);
$scope.time = parseInt(time) + parseInt($scope.time);
}
}]);
Probably pretty easy but I can't wrap my head around it. Thanks.
Use a boolean flag:
$scope.selected = false;
$scope.add = function(price) {
//If item is currently selected, subtract price
if ($scope.selected) $scope.total_price -= price;
//If item is currently NOT selected, add price
else $scope.total_price += price;
$scope.selected = !$scope.selected;
};
Related
I have a simple shopping AngularJs app. It displays the list of products as intended. You can add a product to the cart and it displays the product name and price in the cart and updates the total price. However, if you add a second product, it doesn't display the second product's name. It just adds the previously added product's price to the total instead of the new product price. So it doesn't ng-repeat the products you push into $scope.cart.
Here is my HTML:
<div class="row">
<div class="col-sm-8" ng-repeat="category in categories">
<h2>{{category.categoryName}}</h2>
<div ng-repeat="product in category.products track by $index">
<div class="panel">
<img ng-src="{{product.image}}">
<h6>{{product.name}}</h6>
<p>{{product.price}}</p>
<button class="btn btn-sm" ng-click="addItemToCart(product)">Add to Cart</button>
</div>
</div>
</div>
<div class="col-sm-4">
<ul class="list-group" ng-repeat="products in cart">
<li class="list-group-item">
{{products.name}} |{{products.count}}| {{products.price*products.count}}
<input class="btn btn-sm btn-danger" type="button" ng-click="removeItemCart(products)" value="Remove" />
</li>
<h5>Number of items in cart: {{productsNumber}}</h5>
</ul>
<h4>Total: R{{total}}.</h4>
<button class="btn btn-sm btn-success">Checkout</button>
</div>
</div>
</div>
Here is my AngularJs:
(function(){
var app = angular.module('myApp', ['ngCookies']);
app.controller('productsCtrl', ['$scope','$cookies', function($scope,$cookies){
$scope.categories = productsData;
// Add ids to the array objects //
$scope.categories.forEach( function(d, i){ d.id = i+1; });
$scope.cart = [];
$scope.total = 0;
$scope.productsNumber = $scope.cart.length;
if (!angular.isUndefined($cookies.get('cart'))) {
$scope.cart = $cookies.getObject('cart');
}
$scope.addItemToCart = function(product){
if ($scope.cart.length === 0){
product.count = 1;
$scope.cart.push(product);
} else {
var repeat = false;
for(var i = 0; i< $scope.cart.length; i++){
if($scope.cart[i].id === product.id){
repeat = true;
$scope.cart[i].count +=1;
}
}
if (!repeat) {
product.count = 1;
$scope.cart.push(product);
}
}
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() + 1);
$cookies.putObject('cart', $scope.cart, {'expires': expireDate});
$scope.cart = $cookies.getObject('cart');
$scope.total += parseFloat(product.price);
$cookies.put('total', $scope.total, {'expires': expireDate});
};
$scope.removeItemCart = function(product){
if(product.count > 1){
product.count -= 1;
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() + 1);
$cookies.putObject('cart', $scope.cart, {'expires': expireDate});
$scope.cart = $cookies.getObject('cart');
}
else if(product.count === 1){
var index = $scope.cart.indexOf(product);
$scope.cart.splice(index);
expireDate = new Date();
expireDate.setDate(expireDate.getDate() + 1);
$cookies.putObject('cart', $scope.cart, {'expires': expireDate});
$scope.cart = $cookies.getObject('cart');
}
$scope.total -= parseFloat(product.price);
$cookies.put('total', $scope.total, {'expires': expireDate});
};
}]);
var productsData = [
{
categoryName: "Gas Cylinders",
products: [
{
name: "15kg Gas Cylinder",
image: "images/gas4.jpg",
price: 50
},
{
name: "DEFY 4 Burner Gas Stove",
image: "images/stove2.jpg",
price: 90
}
]
},
{
categoryName: "Electronics",
products: [
{
name: "Samsung Galaxy J4 Core",
image: "images/j4-core.jpg",
price: 100
},
{
name: "40'' FHD Hisense TV",
image: "images/tv2.jpg",
price: 200
}
]
}
];
})();
I have tried to dynamically add product ids to the $scope.cart but it doesn't help.
The first issue I see is, when you're adding id in this line: $scope.categories.forEach( function(d, i){ d.id = i+1; }); it is adding id at category level not at products array inside it. The productsData is an array of array. The inner array has products that customers will add to cart and it should have IDs. Not at the outer array level.
So the logic to assign IDs to products should change. Else the JS code which is based on IDs will fail.
I'm trying to make block with the prices. The unit price varies depending on its quantity of units. For example:
Quantity — Price for each
1____________________$110
10___________________$105
20___________________$100
...
Number of items:__
Total:
Price for each:
There is a need to write a the text field into which the user enters the number of items, and everything is recalculating and summing on the fly.
Here is my realization of this task:
var price1 = 110,
price2 = 105,
price3 = 100,
qty1 = 1,
qty2 = 10,
qty3 = 20;
function conversion(val) {
var div = document.getElementById("div"),
price = document.getElementById("price");
if (isNaN(val)) {
div.innerHTML = "";
price.innerHTML = "";
} else {
switch (true) {
case (val <= 0):
{
div.innerHTML = "";
price.innerHTML = "";
break;
}
case (val >= qty1 && val < qty2):
{
div.innerHTML = val * price1;
price.innerHTML = price1;
break;
}
case (val >= qty2 && val < qty3):
{
div.innerHTML = val * price2;
price.innerHTML = price2;
break;
}
case (val >= qty3):
{
div.innerHTML = val * price3;
price.innerHTML = price3;
break;
}
}
}
}
<div>
Quantity — Price for each
</div>
<div>
<div>1 — $110</div>
<div>10 — $105</div>
<div>20 — $100</div>
</div>
<div>
Number of items:
<div>
<input id="txt" onblur="conversion(this.value)" onchange="conversion(this.value)" onkeypress="conversion(this.value)" onkeyup="conversion(this.value)" type="number">
</div>
</div>
<div>
Total:
<div id="div"></div>
</div>
<div>
Price for each:
<div id="price"></div>
</div>
How it can be properly implemented, taking into account the fact that the lines with the quantity and unit price can be from one to infinity (values are taken from the database)?
I think it is possible to record the price and quantity in data-atributes and parse it with JS. Like this:
...
<div data-quantity="10" data-price="105">
<span class="quantity">10</span>
<span class="price">105</span>
</div>
...
Thanks!
Using the data attribute is indeed a solution:
console.log(document.getElementById("test").dataset)
<div data-quantity="10" data-price="105" id="test">
<span class="quantity">10</span>
<span class="price">105</span>
</div>
It's not fully compatible with previous IE version though, so be careful with it.
I would however suggest that you look for a way of moving your calculations away from the DOM to speed up your calculations.
For instance, parsing the data to a JavaScript object and doing the calculations there would save you some DOM trips and thus speed:
console.clear();
//markup for product
function Product(name) {
return {
//Name of product
name : name,
//List of price ranges (populated later)
prices : [
],
//method for adding a price
newPrice : function newPrice(quantity, cost) {
//Push new price unto list
this.prices.push({
quantity : quantity,
cost : cost
});
//Sort list
this.prices = this.prices.sort(function (a, b) {
return a.quantity - b.quantity
});
},
//Get the price for a variable quantity of this product
get : function (quantity) {
//Loop through prices to find the most matching
var price = 0;
for (var i = 0; i < this.prices.length; i++) {
if (this.prices[i].quantity <= quantity) {
price = this.prices[i].cost;
} else {
break;
}
}
console.log('price per unit:', price, 'price for all', quantity, 'units:', price * quantity)
}
};
} //Make an instance
var myHotProduct = new Product('Fancy pants');
//Add some prices
myHotProduct.newPrice(0, 110);
myHotProduct.newPrice(10, 105);
myHotProduct.newPrice(20, 100);
//get some quantities
myHotProduct.get(0);
myHotProduct.get(1);
myHotProduct.get(9);
myHotProduct.get(10);
myHotProduct.get(19);
myHotProduct.get(20);
//Log everything we know about our product
console.log(myHotProduct);
Now you can get your prices as arrays and modify them outside of the limitations of data-.
I am building an app using google's QPX express and I created a service to call the QPX web service.
I noticed that when I inspect certain functions, I see that they are executing indefinitely. The functions are $scope.pageArray, $scope.humanizeTime.Can someone help me identify why this is the case.
I have an understanding of why this is happening, but am not able to identify the root cause. Somehow/Somewhere in the code I am suggesting to Angular that the model has changed and therefore Angular is running a $scope.$digest, but I cant seem to identify where.
var resultController =planeSearchControllers.controller('resultController',['$scope','$http','commonSharedService','flight', function($scope,$http,commonSharedService,flight){
var isDebugEnabled = true;
$scope.showResults = false;
$scope.showPlaneSearch = true;
$scope.showPlaneError = false;
$scope.planeView = false;
$scope.historyView = false;
$scope.$watch(function() {return commonSharedService.getMode();},function(newValue,oldValue){
console.log('New Mode is' + newValue);
if(newValue == 'plane'){
$scope.planeView = true;
$scope.historyView = false;
$scope.historyObj = [];
}else if(newValue == 'history'){
getHistory(commonSharedService.getUserName());
$scope.planeView = false;
$scope.historyView = true;
}
});
$scope.$watch(function (){return commonSharedService.getValidateInputs();},function (newValue,oldValue){
if(isDebugEnabled){
console.log('Value is changed for getValidateInputs ' + 'New Value is -->'+ newValue);
}
$scope.validateInputs = newValue;
if($scope.validateInputs == true) {
makePlaneCall();
$scope.showResults = true;
commonSharedService.setValidateInputs(undefined);
$scope.errorMsg = commonSharedService.getErrorMsg();
}
if($scope.validateInputs == false) {
$scope.showResults = false;
commonSharedService.setValidateInputs(undefined);
$scope.errorMsg = commonSharedService.getErrorMsg();
}
});
$scope.humanizeTime = function(time){
//var duration = new moment.duration(time, "minutes");
//var hours = duration.hours();
//var minutes = duration.minutes();
var hours = Math.floor(time/60);
var minutes = time - (60 * hours);
var str = hours == 0 ? '': hours + 'hours ' ;
str += minutes == 0 ? '': minutes + 'minutes';
return str;
};
//Page Filtering
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
$scope.numPerPage = 5;
$scope.numPages = function () {
if($scope.tripOption!=null )
return Math.ceil($scope.tripOption.length / $scope.numPerPage);
else
return 0;
};
$scope.pageArray = function () {
var input = [];
for(var i=0;i<$scope.numPages();i++){
input[i] = i+1;
}
return input;
};
var paging = function(arrayIn,pageNo,perPageNo){
var outArray = [];
if(arrayIn!=undefined){
var from = perPageNo * (pageNo-1);
var to = from + perPageNo;
if (to > arrayIn.length)
to= arrayIn.length;
//console.log(from);
//console.log(to);
//console.log(outArray);
for (var i =from; i<to ;i++)
outArray.push(arrayIn[i]);
}
return outArray;
};
$scope.paginationFilter = function (){
return paging($scope.tripOption,$scope.currentPage,$scope.numPerPage);
};
var makePlaneCall = function () {
$scope.appendObj = commonSharedService.getAppendObj();
$scope.jsonObj = commonSharedService.getJsonObj();
$scope.jsonObj['time'] = moment().format("ddd Do,YYYY HH:mm a");
var user = commonSharedService.getUserName();
if(user != undefined)
setHistory(user,$scope.jsonObj);
$scope.planeRequest = {};
$scope.requestObj = {};
var slice = [];
var slice1 ={};
var slice2 ={};
var slice3 ={};
{
slice1['origin'] = $scope.appendObj['departAirport'];
slice1['destination']= $scope.appendObj['multiCity'] ? $scope.appendObj['interimAirport'] :$scope.appendObj['arrivalAirport'];
slice1['date']= $scope.appendObj['departureDate'];
slice1['permittedDepartureTime'] ={
"earliestTime": $scope.appendObj['departureEarliest']
};
if($scope.appendObj['preferredCabin']!=undefined){
slice1['preferredCabin'] = $scope.appendObj['preferredCabin'];
}
slice.push(slice1);
}
if($scope.appendObj['multiCity'] == true){
slice2['origin'] = $scope.appendObj['interimAirport'];
slice2['destination']= $scope.appendObj['arrivalAirport'];
slice2['date']= $scope.appendObj['interimDate'];
slice2['permittedDepartureTime'] ={
"earliestTime": $scope.appendObj['interimEarliest']
};
if($scope.appendObj['preferredCabin']!=undefined){
slice2['preferredCabin'] = $scope.appendObj['preferredCabin'];
}
slice.push(slice2);
}
if($scope.appendObj['isReturnFlight'] == 'true'){
slice3['origin']=$scope.appendObj['arrivalAirport'];
slice3['destination'] = $scope.appendObj['departAirport'];
slice3['date']=$scope.appendObj['arrivalDate'];
slice3['permittedDepartureTime'] ={
"earliestTime": $scope.appendObj['arrivalEarliest']
};
if($scope.appendObj['preferredCabin']!=undefined){
slice3['preferredCabin'] = $scope.appendObj['preferredCabin'];
}
slice.push(slice3);
}
for(var property in $scope.jsonObj){
if($scope.jsonObj.hasOwnProperty(property)){
$scope.requestObj[property] = $scope.jsonObj[property];
}
}
$scope.requestObj['slice'] = slice;
//$scope.requestObj['passengers'] = $scope.jsonObj['passengers'];
$scope.requestObj['solutions'] = 5;
$scope.requestObj['refundable'] = false;
$scope.planeRequest['request'] =$scope.requestObj;
flight.search($scope.planeRequest,function(response){
$scope.result= response;
$scope.info = $scope.result.trips.data;
$scope.tripOption = $scope.result.trips.tripOption;
//console.log($scope.tripOption);
if($scope.tripOption!=null){
{
$scope.airport = $scope.info.airport;
$scope.city = $scope.info.city;
$scope.aircraft = $scope.info.aircraft;
$scope.tax = $scope.info.tax;
$scope.carrier = $scope.info.carrier;
$scope.showPlaneError = false;
$scope.paginationFilter();
}
}
else{
$scope.showPlaneError = true;
$scope.planeSearchErrorMsg = "No Solutions found. Please check your airport codes and set more liberal parameter for the search to see if something turns up.";
}
console.log(response);
},function(response){
console.log("error");
$scope.result= response;
console.log(response);
});
};
function setHistory(userName,historyObj){
var firstTime=true;
var ref = new Firebase("http://flight-searchdb.firebaseIO.com/History");
var historyRef = ref.child(userName);
historyRef.on("value", function(historySnapshotObj) {
if(firstTime==true){
var historySnapshot = historySnapshotObj.val();
console.log(historySnapshot);
var count;
if(historySnapshot!=null)
count = historySnapshot['count'];
console.log(count);
var obj ={};
if(count == undefined) {
obj['count'] = 0;
obj[0]= historyObj;
}else if(count < 9){
obj['count'] = ++count;
obj[count]= historyObj;
}else if(count == 9){
console.log(3);
obj['count'] = count;
for(var i=0;i<9;i++)
obj[i+1] = historySnapshot[i];
obj[0] = historyObj;
}
firstTime = false;
historyRef.update(obj);
}
else {
console.log("Wrong Place");
}
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
function getHistory(userName){
var ref = new Firebase("http://flight-searchdb.firebaseIO.com/History");
var usersRef = ref.child(userName);
usersRef.on("value", function(snapshot) {
for (var i=0;i<10;i++){}
var userHistory = snapshot.val();
var count;
var array=[];
if(userHistory!=null)
count = userHistory['count'];
if (count!=undefined) {
for (var i=0;i <count ; i++)
array.push(userHistory[i]);
}
$scope.historyObj = array;
$scope.$digest();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
}]);
I tested all functions and all of them seem to be working , except that when I added the pagination I dont see any result.
P.S : I was using a filter before , but for the sake of debug , I moved the pagination logic into the controller. I also understand that I could have used a directive.(since I am displaying the result at only place, I decided to skip it.)
I am also adding the view below , in which I am using the controller.
<!-- Result Body-->
<div class="col-sm-6 col-md-6 col-lg-7" data-ng-controller="resultController">
<div class="container-fluid">
<div data-ng-show="planeView">
<div data-ng-hide="showResults">
<div><span></span><span>{{errorMsg}}</span></div>
</div>
<div data-ng-show="showResults">
<div class="showPlaneSearch" data-ng-show="showPlaneSearch">
<div class="query thumbnail">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<span >Page</span>
<select data-ng-model="currentPage" id="selectPage" class="form-control col-xs-5 col-sm-5 col-md-5 col-lg-5"
data-ng-options="value for value in pageArray()" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Select Page Number">
</select>
</div>
</div>
</div>
<ul class="planesResult">
{{currentPage}}
{{numPerPage}}
<li ng-repeat="trip in paginationFilter" class="thumbnail">
<div class="row phoneContents">
<!-- Image -->
<div class="hidden-xs hidden-sm hidden-md col-lg-2">
<img src="images/Airplane-Icon.png" />
</div>
<!-- Trip Total $$$ -->
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-10" >
<span class="price">{{trip.saleTotal}}</span>
</div>
<!-- Everything except Image -->
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-10">
<!--Each Slice -->
<div ng-repeat="slice in trip.slice" class="slice row">
<!-- Each Segment Origin-->
<span class="col-xs-hidden col-sm-4 col-md-4 col-lg-4">
<span ng-repeat="segment in slice.segment">
<span > {{segment.leg[0].origin}}--></span>
<span ng-show="$last"> {{segment.leg[0].destination}} </span>
</span>
</span>
<!-- Each Segment Origin-->
<span class="col-xs-12 col-sm-3 col-md-3 col-lg-3">{{humanizeTime(slice.duration)}}</span>
<span ng-repeat="segment in slice.segment" class="col-xs-hidden col-sm-4 col-md-4 col-lg-4">
<span ng-show="$first"> Depart at {{}} </span>
</span>
<br>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="showPlaneError" data-ng-show="showPlaneError">
<span class="thumbnail">{{planeSearchErrorMsg}}</span>
</div>
</div>
</div>
<div data-ng-show="historyView">
<pre>{{historyObj | json}}</pre>
</div>
</div>
</div>
I can't run code, but i see something suspicious, worth changing:
two places:
$scope.paginationFilter = function (){
return paging($scope.tripOption,
and
if($scope.tripOption!=null){
{
$scope.airport = $scope.info.airport;
$scope.city = $scope.info.city;
$scope.aircraft = $scope.info.aircraft;
$scope.tax = $scope.info.tax;
$scope.carrier = $scope.info.carrier;
$scope.showPlaneError = false;
$scope.paginationFilter();
I see that when tripOption!= null you call paginationFilter function, which uses tripOption.
I have a page with two columns of a fixed height. The items in each column are ng-repeated.
How can I scroll within each column to a certain id? is that possible with AngularJS?
Code
<div>
Scroll to a position
Column #: <input style="width: 20px;">
Item #: <input style="width: 20px;">
</div>
<div class='column'>Column one
<div id="col-1-{{$index}}" class='item' ng-repeat='item in itemsOne track by $index'>
{{$index}} ..... {{item}}
</div>
</div>
<div class='column'>Column two
<div id="col-2-{{$index}}" class='item' ng-repeat='item in itemsTwo track by $index'>
{{$index}} ..... {{item}}
</div>
</div>
JS:
app.controller( 'myCtrl', [ '$scope', function ( $scope ){
$scope.value = 'test';
$scope.itemsOne = [];
$scope.itemsTwo = [];
for(var i=0; i<10; i++){
$scope.itemsOne.push(makeSentence());
$scope.itemsTwo.push(makeSentence());
}
function makeSentence() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < Math.random() * 200; i++ ){
for( var i=0; i < Math.random() * possible.length; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
text += " ";
}
//console.log(text);
return text;
}
$scope.scrollMeTo = function(column, row){
// scroll me to an area in a column
};
}] );
http://plnkr.co/edit/WYtntRagJdQoK7k6fAPc?p=preview
I changed your plunkr to make it work, there is room for a lot of improvement, but please take notice that when you need to handle things in the DOM, you must always do that using directives, as the one I made:
app.directive('autoScrollTo', function () {
return function(scope, element, attrs) {
scope.$watch(attrs.autoScrollTo, function(value) {
if (value) {
var pos = $("#" +attrs.prefixId +value, $(element)).position().top + $(element).scrollTop() - $(element).position().top;
$(element).animate({
scrollTop : pos
}, 1000);
}
});
}
});
See it running: http://plnkr.co/edit/2gb8ZdZ5DPanRBVQvTwa?p=preview
Hope that helps.
I have some divs in the page that show different things of the same kind, for example offers, now offers have ending time, and also posted time, if the user wants to order by ending time, or posted time, they should be re ordered.
I'm looking for a javascript solution that could do that, any particular libraries under Ext JS , or JQuery would work
Here is how these divs look like
<div data-sortunit="1" data-sort1="40" data-sort2="156" data-sort3="1"
data-sort4="1317620220" class="item">
</div>
<div data-sortunit="2" data-sort1="30" data-sort2="116" data-sort3="5"
data-sort4="1317620220" class="item">
</div>
<div data-sortunit="3" data-sort1="10" data-sort2="157" data-sort3="2"
data-sort4="1317620220" class="item">
</div>
So I wanna be able to sort these divs based on data-sortN, N being an integer
Edit: OK, now that you've supplied some HTML, here's javascript code that will sort that specific HTML by the desired column number:
function sortByDataItem(containerID, dataNum) {
var values = [];
$("#" + containerID + " .item").each(function(index) {
var item = {};
item.index = index;
item.obj = this;
item.value = $(this).data("sort" + dataNum);
values.push(item);
});
values.sort(function(a, b) {return(b.value - a.value);});
var container = $("#" + containerID);
for (var i = 0; i < values.length; i++) {
var self = $(values[i].obj);
self.detach();
container.prepend(self);
}
return;
}
$("#sort").click(function() {
var sortValue = $("#sortColumn").val();
if (sortValue) {
sortValue = parseInt(sortValue, 10);
if (sortValue && sortValue > 0 && sortValue <= 3) {
sortByDataItem("container", sortValue);
return;
}
}
$("#msg").show(1).delay(5000).fadeOut('slow');
});
You can see it work here in a jsFiddle: http://jsfiddle.net/jfriend00/JG32X/
Since you've given us no HTML to go on, I've made my own HTML and shown you how you can use jQuery to sort:
HTML:
<button id="sort">Sort</button><br>
<div id="productList">
<div class="row"><div class="productName">Popcorn</div><div class="price">$5.00</div></div>
<div class="row"><div class="productName">Peanuts</div><div class="price">$4.00</div></div>
<div class="row"><div class="productName">Cookie</div><div class="price">$3.00</div></div>
<div class="row"><div class="productName">Beer</div><div class="price">$5.50</div></div>
<div class="row"><div class="productName">Soda</div><div class="price">$4.50</div></div>
</div>
Javascript (run after page is loaded):
$("#sort").click(function() {
var prices = [];
// find all prices
$("#productList .price").each(function(index) {
var str = $(this).text();
var item = {};
var matches = str.match(/\d+\.\d+/);
if (matches && matches.length > 0) {
// parse price and add it to the prices array
item.price = parseFloat(matches[0]);
item.row = $(this).closest(".row").get(0);
item.index = index;
prices.push(item);
}
});
// now the prices array has all the prices in it
// sort it using a custom sort function
prices.sort(function(a, b) {
return(a.price - b.price);
});
// now pull each row out and put it at the beginning
// starting from the end of the prices list
var productList = $("#productList");
for (var i = prices.length - 1; i >= 0; i--) {
var self = $(prices[i].row);
self.detach();
productList.prepend(self);
}
});
And, a jsFiddle that shows it in action: http://jsfiddle.net/jfriend00/vRdrA/.
I made a tiny jqueryPlugin out of jfriend00's answer:
(function($){
$.fn.sortChildrenByDataKey = function(key, desc){
var i, els = this.children().sort(function(a, b) {return (desc?1:-1)*($(a).data(key) - $(b).data(key));});
for (i = 0; i < els.length; i++) {
this.prepend($(els[i]).detach());
}
return this;
};
})(jQuery);
Your HTML:
<div id="myContainer">
<div data-myKey="4"> ... </div>
<div data-myKey="2"> ... </div>
...
</div>
Usage:
$('div#myContainer').sortChildrenByDataKey('myKey', true_or_false);
The children of the container can be any Elements. Its only important, that they are immediate children and have data-X key.
Thank you, jfriend00!!