How to fix toggle() using javascript? [duplicate] - javascript

The jQuery documentation for the .toggle() method states:
The .toggle() method is provided for convenience. It is relatively straightforward to implement the same behavior by hand, and this can be necessary if the assumptions built into .toggle() prove limiting.
The assumptions built into .toggle have proven limiting for my current task, but the documentation doesn't elaborate on how to implement the same behavior. I need to pass eventData to the handler functions provided to toggle(), but it appears that only .bind() will support this, not .toggle().
My first inclination is to use a flag that's global to a single handler function to store the click state. In other words, rather than:
$('a').toggle(function() {
alert('odd number of clicks');
}, function() {
alert('even number of clicks');
});
do this:
var clicks = true;
$('a').click(function() {
if (clicks) {
alert('odd number of clicks');
clicks = false;
} else {
alert('even number of clicks');
clicks = true;
}
});
I haven't tested the latter, but I suspect it would work. Is this the best way to do something like this, or is there a better way that I'm missing?

Seems like a reasonable way to do it... I'd just suggest that you make use of jQuery's data storage utilities rather than introducing an extra variable (which could become a headache if you wanted to keep track of a whole bunch of links). So based of your example:
$('a').click(function() {
var clicks = $(this).data('clicks');
if (clicks) {
alert('odd number of clicks');
} else {
alert('even number of clicks');
}
$(this).data("clicks", !clicks);
});

Here is a plugin that implements an alternative to .toggle(), especially since it has been removed in jQuery 1.9+.
How to use:
The signature for this method is:
.cycle( functions [, callback] [, eventType])
functions [Array]: An array of functions to cycle between
callback [Function]: A function that will be executed on completion of each iteration. It will be passed the current iteration and the output of the current function. Can be used to do something with the return value of each function in the functions array.
eventType [String]: A string specifying the event types to cycle on, eg. "click mouseover"
An example of usage is:
$('a').cycle([
function() {
alert('odd number of clicks');
}, function() {
alert('even number of clicks');
}
]);
I've included a demonstration here.
Plugin code:
(function ($) {
if (!Array.prototype.reduce) {
Array.prototype.reduce = function reduce(accumulator) {
if (this === null || this === undefined) throw new TypeError("Object is null or undefined");
var i = 0,
l = this.length >> 0,
curr;
if (typeof accumulator !== "function") // ES5 : "If IsCallable(callbackfn) is false, throw a TypeError exception."
throw new TypeError("First argument is not callable");
if (arguments.length < 2) {
if (l === 0) throw new TypeError("Array length is 0 and no second argument");
curr = this[0];
i = 1; // start accumulating at the second element
} else curr = arguments[1];
while (i < l) {
if (i in this) curr = accumulator.call(undefined, curr, this[i], i, this);
++i;
}
return curr;
};
}
$.fn.cycle = function () {
var args = Array.prototype.slice.call(arguments).reduce(function (p, c, i, a) {
if (i == 0) {
p.functions = c;
} else if (typeof c == "function") {
p.callback = c;
} else if (typeof c == "string") {
p.events = c;
}
return p;
}, {});
args.events = args.events || "click";
console.log(args);
if (args.functions) {
var currIndex = 0;
function toggler(e) {
e.preventDefault();
var evaluation = args.functions[(currIndex++) % args.functions.length].apply(this);
if (args.callback) {
callback(currIndex, evaluation);
}
return evaluation;
}
return this.on(args.events, toggler);
} else {
//throw "Improper arguments to method \"alternate\"; no array provided";
}
};
})(jQuery);

Related

What is the proper way to look for undefined value

I have the following code:
if (array.indexOf("undefined")===-1){
do something...
}
My initial arrays is this:
array=[,,,,,,];
It gets filled with values as the program goes on but i want the function to stop when there are no undefined spaces. The above syntax though is not correct. Anybody can tell me why.
Your code looks for the string "undefined". You want to look for the first index containing an undefined value. The best way is to use findIndex:
if(array.findIndex(x=>x===undefined) !== -1) {
//do something
}
for(var ind in array) {
if(array[ind] === undefined) {
doSomething();
}
}
Your check didn't work because you passed the string "undefined" instead the the value itself. Also, .indexOf() is designed to explicitly ignore holes in the array.
It seems wasteful to use iteration to detect holes in the array. Instead you could just track how many holes have been filled by using a counter, and execute your code when the counter matches the length.
Either way, the proper way to detect a hole at a particular index is to use the in operator.
Here's a class-based solution for reuse:
class SpacesArray extends Array {
constructor(n, callback) {
super(n);
this.callback = callback;
this.counter = 0;
}
fillHole(n, val) {
if (!(n in this)) {
this.counter++;
console.log("filling hole", n);
}
this[n] = val;
if (this.counter === this.length) {
this.callback();
}
}
}
// Usage
var sa = new SpacesArray(10, function() {
console.log("DONE! " + this);
});
// Emulate delayed, random filling of each hole.
for (let i = 0; i < sa.length; i++) {
const ms = Math.floor(Math.random() * 5000);
setTimeout(() => sa.fillHole(i, i), ms);
}

Add callback to for loop function

I have a function to which I pass an array or an object, then it looks for specific keys and edits their values accordingly,
function iterate(obj, delta) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property],delta);
} else {
if(property === 'unix_time'){
var bee = parseInt(obj[property]);
var b = bee + parseInt(delta);
obj[property] = b;
}
}
}
}
}
Basically, it looks for the "unix_time" key and add a number "delta" to it.
Question: When I call it asynchronous, it becomes undefined, How can I add a callback that I can simply use to determine that the function has finished executing. Or maybe should I add a promise to it?
For example when i run this it returns perfectly
console.log("new one", obj);
iterate(obj, 3600000)
But this is a problem, it becomes undefined
var dd = iterate(obj, 3600000);
res.status(200).send(JSON.stringify(dd));
As mentioned in comments, you function is synchronous and it returns immediately after you call it like this:
var result = iterate(tree, delta);
However, as it's currently written, the result variable will have value of undefined since your iterate function doesn't return anything.
If you have the setup like this:
var obj = {...};
iterate(obj, 3600000)
console.log(obj) // correctly outputs modified object
It will output modified object, since you're not using here the returned value from the function. However, in this scenario:
console.log("new one", iterate(obj, 3600000)); // ouputs `undefined`
the returned value is used and it's undefined.
Using the use case you provided, you can modify the usage like this:
iterate(obj, 3600000);
res.status(200).send(JSON.stringify(obj));
and it will work fine. Or you need to modify iterate to return value. Provide an example of obj so I can write a modification to your iterate function.
Modified the iterate function:
function iterate(obj, delta) {
obj.forEach(function (element) {
if (element.hasOwnProperty('unix_time')) {
element['unix_time'] = parseInt(element['unix_time']) + parseInt(delta);
}
});
return obj;
}
I don't know I understand your question. But, if you want to use a callback, you should split this funcion in two. One for main operation and another for recursivity.
i.e.
function iterate(obj, delta, callback) {
interate_recursive(obj, delta);
if(typeof callback != 'undefined')
return callback();
else return obj;
}
function interate_recursive(obj,delta){
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property],delta);
} else {
if(property === 'unix_time'){
var bee = parseInt(obj[property]);
var b = bee + parseInt(delta);
obj[property] = b;
}
}
}
}
}

How Would I Make an If Statement From Arguments in Javascript?

I need to pass certain parameters into a function and have that function pull from an array based on the arguments passed to it. It's hard to explain, so I'll show you what I'm trying to do.
function SearchDeck(deck,...){
var tryagain = true;
do{
if(deck[0].property == value){
//do something;
tryagain = false;
}
else{
deck.splice(0,1);
}
}
while(tryagain);
}
There are multiple decks to look in, the proper deck will be passed in. I want to always be drawing off the top of the deck (index 0 of the array). I need to draw continuously until I find a card that matches what I'm after. I splice out the 0 index if it doesn't match. What I'm after is dynamic, varying across the properties or even the operators I would use.
Some examples of if statements I would have are...
deck[0].color == "orange"
deck[0].value >= 5
deck[0].value < -4
I could make multiple functions or have the function fork based on an argument, but that doesn't seem like the best way to go about this.
If I'm understanding this correctly, you want the behavior of the if(deck[0].property == value) to be different for each invocation of the SearchDeck(...) function?
My recommendation would be to pass in a function:
function SearchDeck(deck, validationFunction, ...){
var tryagain = true;
do{
if(validationFunction(deck[0])){
//do something;
tryagain = false;
}
else{
deck.splice(0,1);
}
}
while(tryagain);
}
Then when you call the code, you can do:
SearchDeck(deck, function(firstCard) { return firstCard.color == "orange" }, ...);
SearchDeck(deck, function(firstCard) { return firstCard.value >= 5 }, ...);
SearchDeck(deck, function(firstCard) { return firstCard.value < -4 }, ...);
Or, if the cases you're looking for might be reused, it might also be cleaner to make those named functions:
function validateColor(firstCard) {
return firstCard.color == "orange";
}
function validateHighValue(firstCard) {
return firstCard.value >= 5;
}
function validateLowValue(firstCard) {
return firstCard.value < -4;
}
SearchDeck(deck, validateColor, ...);
SearchDeck(deck, validateHighValue, ...);
SearchDeck(deck, validateLowValue, ...);
It sounds like you may be interested in the typeof operator:
if (typeof deck == 'object') { ... }
if (typeof deck[0].color == 'string') { ... }
if (typeof deck[0].value == 'number') { ... }
Alternatively:
if (deck[0].hasOwnProperty('color')) { ... }
This is what I came up with. You need to push the way of check (either "== 'oragne'" or "<3") as string.
function searchDeck() {
var deck = Array.prototype.slice.call(arguments, 1),
tryagain = true,
string = deck[deck.length - 1];
deck.pop();
while (tryagain) {
if (eval('deck[0].property' + string)) {
//do something;
alert('card found');
tryagain = false;
} else {
deck.splice(0, 1);
}
}
}
Hope this is what you wanted ;)
Here is a working jsfiddle example, of course there might be a more elegant way, this is just what I came up with. Note that eval can be dangerous, so you should be carefull if user picks what the test will be (the string pushed into the array).

Something similar to jQuery when / then: deferred execution with break

I'm searching for a solution where I'm able to run different functions, but some of them need a timeout and all following functions need to wait until the previous one is finished. Every function should be able to break the complete process.
Now I thought pushing all functions to a stack and loop through them:
function foo() {
// bar() should wait as long the following is finished:
setTimeout(function(){
if ((new Date()).getSeconds() % 2) {
alert('foo');
// break loop through functions (bar is not called)
}
else {
// start next function (bar is called)
}
}, 1000);
}
function bar() {
setTimeout(function(){
alert('bar')
}, 1000);
}
var functions = new Array('foo', 'bar');
for (var i = 0, length = functions.length; i < length; i++) {
window[functions[i]]();
}
But how to include wait/break?!
Note: This should work with 2+ functions (amount of functions is changeable)
Note2: I don't want to use jQuery.
Note: I have updated my answer, see bottom of post.
Alright, let's take a look.
You're using the window[func]() method, so you should be able to store and use return values from each function.
Proof:
function a(){
return "value";
}
var ret_val = window['a']();
alert(ret_val);
Let's create a return rule:
If function returns true, continue execution flow.
If function returns false, break execution flow.
function a(){
//Do stuff
return (condition);
}
for(i = 0; i < n; i++){
var bReturn = window['function']();
if(!bReturn) break;
}
Now let's put it into practice.
function a(){
//Do stuff
return ((new Date()).getSeconds() % 2); //Continue?
}
function b(){
//Do stuff
return true; //Continue?
}
function c(){
//Do stuff
return false; //Continue?
}
function d(){
//Do stuff
return true; //Continue?
}
var functions = new Array('a', 'b', 'c', 'd');
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
Depending on when you execute the script, eg, an even or uneven time period, it will only execute function a or execute functions a b & c. In between each function, you can go about your normal business.
Of course, the conditions probably vary from each individual function in your case.
Here's a JSFiddle example where you can see it in action.
With some small modification, you can for instance, make it so that if function a returns false, it will skip the following function and continue on to the next, or the one after that.
Changing
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
To this
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) i++;
}
Will make it skip one function, every time a function returns false.
You can try it out here.
On a side-note, if you were looking for a waiting function that "pauses" the script, you could use this piece of code.
function pausecomp(millis){
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}while(curDate-date < millis);
}
Update
After adjusting the code, it now works with setTimeout.
The idea is that you have an entry point, starting with the first function in the array, and pass along an index parameter of where you currently are in the array and then increment index with one to execute the next function.
Example | Code
function next_function(index){
if(index >= functions.length) return false;
setTimeout(function(){
window[functions[index+1]](index+1);
}, 1000);
}
function a(index){
//Do stuff
if(((new Date()).getSeconds() % 2)) return false; //Stop?
next_function(index);
}
function b(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
function c(index){
//Do stuff
if(true) return false; //Stop?
next_function(index);
}
function d(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
var functions = new Array('a', 'b', 'c', 'd');
//entry point
window[functions[0]](0);
This is exactly the scenario promises solve. In particular, the fact that promises can be broken is perfect for your situation, since a broken promise prevents the chain from continuing (just like a thrown exception in synchronous code).
Example, using the Q promise library discussed in the above-linked slides:
function fooAsync() {
return Q.delay(1000).then(function () {
if ((new Date()).getSeconds() % 2) {
alert("foo");
throw new Error("Can't go further!");
}
});
}
function barAsync() {
return Q.delay(1000).then(function () {
alert("bar");
});
}
var functions = [fooAsync, barAsync];
// This code can be more elegant using Array.prototype.reduce, but whatever.
var promiseForAll = Q.resolve();
for (var i = 0; i < functions.length; ++i) {
promiseForAll = promiseForAll.then(functions[i]);
}
// Of course in this case it simplifies to just
// promiseForAll = fooAsync().then(barAsync);
promiseForAll.then(
function () {
alert("done!");
},
function (error) {
alert("someone quit early!");
// and if you care to figure out what they said, inspect error.
}
).end();

javascript, wait for something to be true then run action

Well the title kindof says what I need. Because in Javascript timeouts asynchronous I need to know when something becomes true. I don't want busyloop.
Came up with:
function do_when(predicate, action, timeout_step) {
if (predicate()) {
action();
} else {
setTimeout(do_when, timeout_step, predicate, action, timeout_step);
}
}
Is it good Javascript or can I make better?
Depending on what the predicate is, you might be able to fit your problem into an implementation of the observer pattern. A while back I wrote a blog post about creating JavaScript objects with observable properties. It really depends on what the predicate is, but this might get you most of the way there with code like this:
var observable = createObservable({ propToWatch: false });
observable.observe('propToWatch', function (oldValue, newValue) {
alert('propToWatch has changed from ' + oldValue + ' to ' + newValue);
});
observable.propToWatch(true); // alert pops
Of course, this might be overkill for your example. Since it's never listed out explicitly (n.b. I am not a very good blogger), here's the complete code needed to make this work:
var createMediator = function () {
var events = {};
return {
subscribe: function (eventName, callback) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
},
publish: function (eventName) {
var i, callbacks = events[eventName], args;
if (callbacks) {
args = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < callbacks.length; i++) {
callbacks[i].apply(null, args);
}
}
}
};
};
var createObservable = function (properties) {
var notifier = createMediator(), createObservableProperty, observable;
createObservableProperty = function (propName, value) {
return function (newValue) {
var oldValue;
if (typeof newValue !== 'undefined' &&
value !== newValue) {
oldValue = value;
value = newValue;
notifier.publish(propName, oldValue, value);
}
return value;
};
};
observable = {
register: function (propName, value) {
this[propName] = createObservableProperty(propName, value);
this.observableProperties.push(propName);
},
observe: function (propName, observer) {
notifier.subscribe(propName, observer);
},
observableProperties: []
};
for (propName in properties) {
observable.register(propName, properties[propName]);
}
return observable;
};
My observable objects make use internally of a small eventing framework (the createMediator function) I wrote once for a project. (Before realizing jQuery supported custom events. D'oh!) Again, this may or may not be overkill for your need, but I thought it was a fun hack. Enjoy!
It's decent enough, if it's easy enough to read and it works just fine then it's generally good javascript.
Performance-wise, it's generally better to call the function whenever whatever is set to true happens. So in whatever function that executes to make predicate() return true, you could just call action() at the end. But I'm sure that's what you would have done if you could, right?
You could also look at using a callback, where you register a javascript function to a particular variable or function argument and when the function is run it executes whatever function was set to the callback variable.
if your predicate become true when a variable change, here is another solution:
say we want to log 'Big brother is watching you' when value of object a become 2.
function observable (value, condition, callback){
this.value = value;
this.condition = condition;
this.callback = callback;
}
observable.prototype = {
get value () {
return this._value;
},
set value (value) {
this._value = value;
if (this.condition && this.callback && this.condition (value)) {
this.callback (value);
}
}
};
condition = function (value) {
console.log ('condition', value);
return value === 2;
}
callback = function (value) {
console.info ('Big Brother is watching you!');
}
var a = new observable (0, condition, callback);
console.log ('set value to 1');
a.value = 1;
console.log ('set value to 2');
a.value = 2;
console.log ('set value to 3');
a.value = 3;
you can try this exemple in firefox

Categories