Combining $.each functions - javascript

I'm using the $.each function in jQuery to assign and change classes and IDs on various elements on click. I was wondering if there is a way to combine these into a single function rather than having three separate functions.
var stored = $.each;
var myFunction = function() {
$(this).removeAttr("id");
};
function numbers() {
//function 1
stored($(".numbers"), function(index, value) {
var num = index + 1 + ".";
$(value).empty();
$(value).append(num);
});
//function 2
stored($(".weight-change"), function(index) {
myFunction();
$(this).attr("id", "weight" + index);
});
//function 3
stored($(".onebox"), function(index) {
myFunction();
$(this).attr("id", "shipprice1box" + index);
});
}

You can make a generic function and call it. There is no need to remove the attribute before you update it. Just a waste of processing time. attr supports a function so no need for each.
function updateIds (selector, prefix) {
$(selector).attr("id", function (index) { return prefix + index; });
}
updateIds(".weight-change", "weight");
updateIds(".onebox", "shipprice1box");

Here's how you can avoid code duplication (you can edit it further according to your needs).
var arr = ['.numbers', '.weight-change', '.onebox'];
stored($(arr.join()), function(index, value) {
if ($(this).is(arr[0])) {
var num = index + 1 + ".";
$(value).empty();
$(value).append(num);
}
else if ($(this).is(arr[1]) || $(this).is(arr[2])) {
myFunction();
if ($(this).is(arr[1])) {
$(this).attr("id", "weight" + index);
}
else if ($(this).is(arr[2])) {
$(this).attr("id", "shipprice1box" + index);
}
}
});

Related

Repeater issue/problm

We are trying to use this repeater:
https://www.jqueryscript.net/form/Form-Fields-Repeater.html
It seems there is problem if you look at the demo.
https://www.jqueryscript.net/demo/Form-Fields-Repeater/
Try on demo to add for example four groups.(press three times add button).
Then remove all four groups by pressing Delete button.
Then press add again to add a new group.
If you inspect the name element it is:
The problem is with test[4]name. Normally it should be test[0]name.
It seems that when you delete element does not delete the counting.
So if you play a little bit with delete/add buttons counting is wrong.
Javascript of this is :
jQuery.fn.extend({
createRepeater: function () {
var addItem = function (items, key) {
var itemContent = items;
var group = itemContent.data("group");
var item = itemContent;
var input = item.find('input,select');
input.each(function (index, el) {
var attrName = $(el).data('name');
var skipName = $(el).data('skip-name');
if (skipName != true) {
$(el).attr("name", group + "[" + key + "]" + attrName);
} else {
if (attrName != 'undefined') {
$(el).attr("name", attrName);
}
}
})
var itemClone = items;
$("<div class='items'>" + itemClone.html() + "<div/>").appendTo(repeater);
};
/* find elements */
var repeater = this;
var items = repeater.find(".items");
var key = 0;
var addButton = repeater.find('.repeater-add-btn');
var newItem = items;
if (key == 0) {
items.remove();
addItem(newItem, key);
}
/* handle click and add items */
addButton.on("click", function () {
key++;
addItem(newItem, key);
});
}
});
Have anyone ever used this repeater? or have a version of this that works correct?

show and hide element options for my case

I'm having a little bit of difficulties when I need to hide an element on a page.
I am using this script to create my multiselect dropdown element which is the main controller for the elements on the page (http://wenzhixin.net.cn/p/multiple-select/docs/#the-basics1).
It returns an array of selected elements and my elements have their showIfValues set in a JSON file.
My functions should do this:
I get selected values from the dropdown element in array (ex. ["value1", "value2"]).
Going through all the elements and find where in showIfValue is any value from the array above, show it
In the change of the multiselect dropdown, if any of the fields are removed, remove the element but leave the rest on the page.
Legend in showHideHendler function:
inp is the id of the input field I would like to show on the page
controlInp is the control input (in this case multiselect dropdown)
value is the array populated with the showIfValues from JSON file of the elements
So far I made it here. These are the things I have implemented.
function diffArray(arr1, arr2) {
return arr1.concat(arr2).filter(function (val) {
if (!(arr1.includes(val) && arr2.includes(val)))
return val;
});
}
function getSelectedValues(controlInput){
if($('#' + controlInput).attr("multiple") === "multiple"){
// var selectValues = $('#' + controlInput).multipleSelect("getSelects");
var selectValues = [];
if($('#' + controlInput).multipleSelect("getSelects") != null) {
selectValues = $('#' + controlInput).multipleSelect("getSelects");
}
return selectValues;
}
}
var multipleShowHideHandler = (function() {
var selectedValues = [];
function setSelectedValues(value){
selectedValues.push(value);
}
function overrideSelected(value){
selectedValues = value;
}
function getSelectedValues(){
return selectedValues;
}
return {
setSelectedValues: setSelectedValues,
getSelectedValues: getSelectedValues,
overrideSelected: overrideSelected
}
})();
function showHideHandler(inp, controlInp, value) {
if (!$('#' + controlInp).is(':checkbox') && !($.isArray(value))) {
value = $.makeArray(value);
}
var selectedValues = getSelectedValues(controlInp);
if(($('#' + controlInp).attr("multiple") === "multiple") && !$.isEmptyObject(selectedValues)){
$('#' + controlInp).change(function(){
var oldState = multipleShowHideHandler.getSelectedValues();
var selectedValues = getSelectedValues(controlInp);
if($.isEmptyObject(oldState)){
$.each(selectedValues, function(i, val){
multipleShowHideHandler.setSelectedValues(val);
});
}
var differentArray = diffArray(selectedValues, oldState);
if(!$.isEmptyObject(differentArray)){
if(($.inArray(differentArray[0], value) !== -1)){
$('#' + inp + 'Container').hide();
}
multipleShowHideHandler.overrideSelected(selectedValues);
}
//check diff
/*if(!$.isEmptyObject(selectedValues) && !$.isEmptyObject(oldState)){
var diff = diffArray(selectedValues, oldState);
}*/
$.each(selectedValues, function(i, val){
if(($.inArray(val, value) !== -1)){
$('#' + inp + 'Container').show();
}
});
});
}else if (($.inArray($('#' + controlInp).val(), value) > -1) || $('#' + controlInp).prop('checked') === value) {
$('#' + inp + 'Container').show();
} else {
$('#' + inp + 'Container').hide();
}
}
This works on some elements, but the moment it overrides my oldState the fields are not hidden.
Any kind of help is much appreciated. Thanks in advance.
After looking and trying many things, I have found that the easiest way is basically to remove all elements and show them again on any change of the multiple select dropdown element.
So the final code looks like this:
if(($('#' + controlInp).attr("multiple") === "multiple") && !$.isEmptyObject(selectedValues)){
$('#' + controlInp).change(function(){
var selectedValues = getSelectedValues(controlInp);
if(!$.isEmptyObject(selectedValues)){
$('#' + inp + 'Container').hide();
$.each(selectedValues, function(i, val){
if(($.inArray(val, value) !== -1)){
$('#' + inp + 'Container').show();
}
});
}else{
$('#' + inp + 'Container').hide();
}
});
}
There is no need to add a before state and after so this is the only thing I need.
DiffArray and multipleShowHideHandler are no longer needed.
Hope this helps someone in the future.

JSON return value to global variable

Simply my code looks like this:
var thevariable = 0;
For(){
//somecode using thevariable
$.getJSON('',{},function(e){
//success and i want to set the returned value from php to my variable to use it in the forloop
thevariable = e.result;
});
}
my main problem that the variable value stays "0", during the whole For loop, while i only want it to be "0" at the first loop, then it takes the result returned from PHP to use it on for loop.
here it my real code if you need to take a look:
var orderinvoice = 0;
for(var i=0; i<table.rows.length; i++){
var ordername = table.rows[i].cells[5].innerText;
var orderqty = ((table.rows[i].cells[1].innerText).replace(/\,/g,'')).replace(/Qty /g,'');
var orderprice = (table.rows[i].cells[2].innerText).replace(/\$/g,'');
var ordertype = table.rows[i].cells[3].innerText;
var orderlink = table.rows[i].cells[4].innerText;
$.getJSON('orderprocess.php', {'invoice': orderinvoice, 'pay_email': email, 'ord_name': ordername, 'ord_qty': orderqty, 'ord_price': orderprice, 'ord_type': ordertype, 'ord_link': orderlink}, function(e) {
console.log();
document.getElementById("result").innerText= document.getElementById("result").innerText + "Order #"+e.result+" Created Successfully ";
document.getElementById("invoker").innerText = ""+e.invoice;
orderinvoice = e.invoice;
if(i+1 == table.rows.length){
document.getElementById("result").innerText= document.getElementById("result").innerText + "With invoice #" + e.invoice;
}
});
in a loop block, before one ajax complete other one will be run and this's javascript natural treatment. For your case you can call a function at the end of success event. Do something like this:
var i = 0;
doSt();
function doSt() {
var orderinvoice = 0;
var ordername = table.rows[i].cells[5].innerText;
var orderqty = ((table.rows[i].cells[1].innerText).replace(/\,/g, '')).replace(/Qty /g, '');
var orderprice = (table.rows[i].cells[2].innerText).replace(/\$/g, '');
var ordertype = table.rows[i].cells[3].innerText;
var orderlink = table.rows[i].cells[4].innerText;
$.getJSON('orderprocess.php', { 'invoice': orderinvoice, 'pay_email': email, 'ord_name': ordername, 'ord_qty': orderqty, 'ord_price': orderprice, 'ord_type': ordertype, 'ord_link': orderlink }, function(e) {
console.log();
document.getElementById("result").innerText = document.getElementById("result").innerText + "Order #" + e.result + " Created Successfully ";
document.getElementById("invoker").innerText = "" + e.invoice;
orderinvoice = e.invoice;
if (i + 1 == table.rows.length) {
document.getElementById("result").innerText = document.getElementById("result").innerText + "With invoice #" + e.invoice;
}
i++;
if (i < table.rows.length) doSt();
});
}
I think you need a recursive function that always deals with the first element in your rows array and then splices it off and calls itself. For example, something like this:
function getStuff(rows, results) {
if (rows.length > 0) {
var ordername = rows[0].cells[5].innerText;
$.getJSON('orderprocess.php', { 'ord_name': ordername }, function (e) {
// do some stuff
results.push('aggregate some things here?');
rows.splice(0, 1);
return getStuff(rows, results);
});
} else {
return results;
}
}
When the array is spent, results will be returned with whatever aggregate you wanted at the end of the cycle. Then, you can do as you please with the results. I think you can also manipulate the DOM inside the function as you see fit if that makes more sense. Hope this helps.

Stopping the Jquery.map when a condition is met

I have the following code:
var parentEls = node.parents()
.map(function () {
var curParentID = this.getAttribute("id");
var curParentClass = this.getAttribute("class");
if(curParentID) {
return this.tagName + "#" + curParentID;
/*stop the map function from proceeding*/
} else {
return this.tagName;
}
})
.get().reverse().join(", ");
The above code helps to find search for unique id and once an ID is spotted , it creates the xpath. How can i stop the the map function once it has come to a point where the id is retrieved and the map function should stop? I tried return false and break but it does not work.
Is there any other suggestion?
I don't think it is possible using .map(), you may have to use .each() here
var temp = [];
node.parents().each(function () {
var curParentID = this.getAttribute("id");
var curParentClass = this.getAttribute("class");
if(curParentID){
temp.push(this.tagName + "#" + curParentID);
return false;
} else {
temp.push(this.tagName);
}
});
var parentEls = temp.reverse().join(", ");
// parentsUntil() doesn't include the element specified by selector,
// so you need to add the closest ancestor with id attribute to the collection
var parentEls = node.parentsUntil('[id]').add( node.closest('[id]') )
.map(function () {
return this.id ? '#' + this.id : this.tagName;
})
.get().join(", ");
Fiddle (updated): http://jsfiddle.net/SKqhc/5/

How can I automate chaining a series of ajax requests?

Look at the lower part of my function: I want to repeat info(url_part1 + next + url_part2, function(next) { couple of times. Is there a smarter way than one presented below (maybe some kind of loop)? I've been thinking whole day and I can't devise anything.
function info(link, callback) {
$.getJSON(link, function(json) {
$.each(json.data.children, function(i, things) {
$("#threadlist").append('<img src="' + things.data.url + '">');
});
callback(json.data.after);
});
}
var url_first = "http://www.reddit.com/r/aww/.json?jsonp=?";
var url_part1 = "http://www.reddit.com/r/aww/.json?after=";
var url_part2 = "&jsonp=?";
info(url_first, function(next) {
info(url_part1 + next + url_part2, function(next) {
info(url_part1 + next + url_part2, function(next) {
info(url_part1 + next + url_part2, function(next) {
info(url_part1 + next + url_part2, function(next) {
});
});
});
});
});
Js fiddle: http://jsfiddle.net/rdUBD/1/
Maybe something like that:
http://jsfiddle.net/JwAbK/
You might want to consider processing them recursively.
function processRequest( request)
{
$.getJSON(request,function(json) {
// process json
if (json.data.after) {
processRequest( "http://www.reddit.com/r/aww/.json?after="
+ json.data.after
+ "&jsonp=?" );
}
});
}
processRequest("http://www.reddit.com/r/aww/.json?jsonp=?");
If I understand correctly it seems that all you want to do is call a function recursively, up to a certain number of times. Here is sample code that does exactly that:
function testFunction(number, callback) {
document.writeln("testFunction() called with number = " + number + "<br />");
callback(number + 1);
}
var CallCount = 0;
testFunction(1, function(next) {
CallCount++;
document.writeln("callback called next = " + next + "<br />");
if(CallCount == 5) {
document.writeln("callback called 5 times, don't call it again");
} else {
testFunction(next, arguments.callee);
}
});
That code outputs the following:
testFunction() called with number = 1
callback called next = 2
testFunction() called with number = 2
callback called next = 3
testFunction() called with number = 3
callback called next = 4
testFunction() called with number = 4
callback called next = 5
testFunction() called with number = 5
callback called next = 6
callback called 5 times, don't call it again
You can try it out at jFiddle: http://jsfiddle.net/luisperezphd/NQya6/
Applying this to your code you get:
var DebugCount = 0;
function EmulateGetJson(link, callback) {
DebugCount++;
callback({
data: {
children: [
{ data : { url: "https://www.google.com/images/srpr/logo3w.png?" + DebugCount, next: "next" } }
]
}
});
}
function info(link, callback) {
//$.getJSON(link, function(json) {
EmulateGetJson(link, function(json) {
$.each(json.data.children, function(i, things) {
$("#threadlist").append('<img src="' + things.data.url + '">');
});
callback(json.data.after);
});
}
var url_first = "http://www.reddit.com/r/aww/.json?jsonp=?";
var url_part1 = "http://www.reddit.com/r/aww/.json?after=";
var url_part2 = "&jsonp=?";
var CallCount = 0;
var TargetCallCount = 5;
info(url_first, function(next) {
CallCount++;
if(CallCount != TargetCallCount) {
info(url_part1 + next + url_part2, arguments.callee);
}
});
The jsFiddle for this can be found at: http://jsfiddle.net/luisperezphd/uY8Kj/
I emulated the call to $.getJSON in JavaScript code in order to provide a complete example. I appended a number to the end of the sample image URL to illustrate that a different URL was being returned each time.
arguments.callee refers to the current function, in this case the function you are passing as the callback to info() this is the key to the recursion.

Categories

Resources