Override jQuery cycle via <a> link - javascript

Is it possible to access the jQuery cycle via a link to override the i variable?
I've seen examples that do this, but not in a case like this where cycle() function is inside a variable:
$(document).ready(function() {
$('div[id^="content-"]').hide();
});
$(window).load(function() {
var divs = $('div[id^="content-"]').hide(),
i = 0;
(function cycle() {
divs.eq(i).fadeIn(400)
.delay(5000)
.fadeOut(400, cycle);
if (typeof window["Stage" + i] != 'undefined')
{
window["Stage" + i].destroy();
}
i = ++i % divs.length; // increment i,
// and reset to 0 when it equals divs.length
if (i == 1)
{
window["Stage" + i] = new swiffy.Stage(document.getElementById('graph_lines'), swiffyobject);
}
else if (i === 0)
{
window["Stage" + i] = new swiffy.Stage(document.getElementById('circle_lines'), circleobject);
}
window["Stage" + i].start();
})();
});
Demo Link

If you're asking whether cycle can be globally accessed, then the short answer is
No
Your cycle function is part of a private function closure. You'll have to make it global for it to work that way. But it's highly recommended that we don't pollute global scope so you should apply this function to your links within that closure:
$(function(){
var divs = $('div[id^="content-"]').hide();
var cycle = function() {
// do the thing although you're using globals etc.
}
$("a").click(cycle);
});
Anyway this is the way to do this, but you will have to clean up your code removing globals unless they're really needed. And learn about jQuery and how it works and don't do same things twice. When you'll learn a little you'll know what I'm talking about.
Note: I'm not sure what you mean by override via a link. I've attached to it to click event in my example, but that may not be what you want. You'll have to be much more specific.
Running swiffy on link click
I suggest you make your life easy on yourself and add links like:
Start swiffy 0
And have this code instead:
$(function() {
var divs = $('div[id^="content-"]').hide();
divs.each(function(index) {
$(this).fadeIn(400)
.delay(5000)
.fadeOut(400);
// replaces first if statement
window["Stage" + index] && window["Stage" + index].destroy();
if (index === 0)
{
window["Stage1"] = new swiffy.Stage(document.getElementById('graph_lines'), swiffyobject);
}
if (index === divs.length - 1)
{
window["Stage0"] = new swiffy.Stage(document.getElementById('circle_lines'), circleobject);
}
window["Stage" + ((index + 1) % divs.length)].start();
});
$("a.start-swiffy").click(function(evt) {
evt.preventDefault();
window["Stage" + $(this).data("swiffy")].start();
});
});
Even though I still don't understand why do you do all those div fades and removing stages and running them in offset sequence by one so they start from the second and the first one starts last? That is the main part that could be improved I guess...

Related

addEventListener (and removeEventListener) function that need param

I need to add some listeners to 8 object (palms).
These object are identical but the behaviour have to change basing to their position.
I have the follow (ugly) code:
root.palmsStatus = ["B","B","B","B","B","B","B","B"];
if (root.palmsStatus[0] !== "N")
root.game.palms.palm1.addEventListener("click", palmHandler = function(){ palmShakeHandler(1); });
if (root.palmsStatus[1] !== "N")
root.game.palms.palm2.addEventListener("click", palmHandler = function(){ palmShakeHandler(2); });
if (root.palmsStatus[2] !== "N")
root.game.palms.palm3.addEventListener("click", function(){ palmShakeHandler(3); });
if (root.palmsStatus[3] !== "N")
root.game.palms.palm4.addEventListener("click", function(){ palmShakeHandler(4); });
if (root.palmsStatus[4] !== "N")
root.game.palms.palm5.addEventListener("click", function(){ palmShakeHandler(5); });
if (root.palmsStatus[5] !== "N")
root.game.palms.palm6.addEventListener("click", function(){ palmShakeHandler(6); });
if (root.palmsStatus[6] !== "N")
root.game.palms.palm7.addEventListener("click", function(){ palmShakeHandler(7); });
if (root.palmsStatus[7] !== "N")
root.game.palms.palm8.addEventListener("click", function(){ palmShakeHandler(8); });
I have two needs:
1) doesn't use an anonymous function on click event.
I wrote this code, but it doesn't work
root.game.palms.palm8.addEventListener("click", palmShakeHandler(8));
So this one works fine
root.game.palms.palm8.addEventListener("click", function(){ palmShakeHandler(8); });
But I didn't understand how remove the event listener.
I try this solution, but it doesn't work
root.game.palms.palm8.addEventListener("click", palmHandler = function(){ palmShakeHandler(8); });
root.game.palms.palm8.removeEventListener("click", palmHandler);
2) add and remove listener in a for cycle
I wrote the follow code but the behaviour is not correct.
for (i=1; i <= root.palmsStatus.length; i++){
if (root.palmsStatus[i-1] !== "N"){
root.game.palms["palm" + i].addEventListener("click", function(){ palmShakeHandler(i); });
}
}
the listeners was added but the value of the parameter passed to the palmShakeHandler is always 8.
Nobody could help me to fix these issues?
There is a actually, a perfect way to do that in JavaScript using the Function.prototype.bind method.
bind let you define extra parameters that will be passed, as arguments, of the function.
You should also keep in mind that bind creates a new function and doesn't modify the initial function.
Here is what it looks like:
function palmHandler(number) {
// your code working with `number`
}
var palmHandler8 = palmHandler.bind(null, 8)
// the palmHandler8 is now tied to the value 8.
// the first argument (here null) define what `this` is bound to in this function
This should fix your problem, and you will be able to remove handlers easily :)
Your code will look like this:
for (i=1; i <= root.palmsStatus.length; i++){
if (root.palmsStatus[i-1] !== "N"){
root.game.palms["palm" + i].addEventListener("click", palmShakeHandler.bind(null, i));
}
}
To be able to remove the handler afterward, you need to keep a reference to the function you create with bind. This would be the way to do this.
var boundHandler = handler.bind(null, i);
element.addEventListener(boundHandler);
element.removeEventListener(bounderHander);
If you want to know more about the awesome bind method in JavaScript, the MDN is your friend :) https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
BTW, the problem with you function always returning 8 is a very common question in JavaScript. This thread will explain everything (spoiler, it's a matter of scoping :) ) https://stackoverflow.com/a/750506/2745879
So in case your array of »palms« is very huge, it is basically a bad Idea to add a single event listener to each of them, because that causes performance flaws. So I would suggest a different approach:
var handlers = [function (e) {}, …, function (e) {}];
root.game.palms.forEach(functiion (palm, idx) {
palm.setAttribute('data-idx', idx);
});
<palmsparent>.addEventListener('click', function (e) {
var c = e.target, idx = -1;
while (c) {
if (c.hasAttribute && c.hasAttribute('data-idx')) {
idx = parseInt(c.getAttribute('data-idx'));
break;
}
c = c.parentNode;
}
//here you also check for the »palm status«
if (idx >= 0) {
handlers[idx](c);
}
})
One event listener for all, much easier to remove and better for performance.
In your last solution you are pasing the same var to every function and that is what make al the functions work with 8 because is the last value of the variable.
To work arround that you can use "let" ( please at least use var, otherside that "i" is global and can be changed every where in the code) but since I dont know wich browser you target I propose other solution.
for (var i=1; i <= root.palmsStatus.length; i++){
if (root.palmsStatus[i-1] !== "N"){
root.game.palms["palm" + i].addEventListener("click", (function(index)
(return function(){
palmShakeHandler(index);
}))(i);
}
}
Since its look like You are targeting modern browsers I will use let.https://kangax.github.io/compat-table/es6/
for (var i=1; i <= root.palmsStatus.length; i++){
let index = i;
let intermediateFunction = function(){palmShakeHandler(index);};
if (root.palmsStatus[i-1] !== "N"){
root.game.palms["palm" + i].addEventListener("click",intermediateFunction);
root.game.palms["palm" + i].removeHandShake = function(){this.removeEventListener("click",intermediateFunction)};
}
}
So now you just need to call "removeHandShake" and will remove the listener,
I have code this right here so it ease some minor errors to pop

Changing Scope from Global to Local Breaking Javascript Program

Thanks to the help of you fine Overflowians, I fixed up my silly little RNG Addition game and got it working. Now, at one user's suggestion, I'm trying to change the scope of the addition game's code from global to local before I code up the next game; I want each game to be completely contained within its own scope, as I understand that learning to not thoughtlessly contaminate the global scope is a good idea. However, I'm a bit stuck on how to achieve that.
Here's the code for the currently functional addition game:
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
And here's my attempt to wrap it, which only succeeds in breaking the game, such that the button doesn't even respond:
window.addEventListener("load", function() {
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
});
Functional code is available on JSFiddle here.
Note that the onLoad option in JSFiddle does the same as your 2nd snippet. You'll want to choose one of the No wrap options when binding to 'load' yourself.
And, the issue stems from attempting to bind to 'load' within a 'load' handler:
window.addEventListener("load", function() {
// ...
window.addEventListener("load", initChallenge);
});
When the event is already firing and handling the outer binding, it's too late to add more handlers to it. They won't be cycled through and the event shouldn't occur again.
You'll either want to call initChallenge within the outer event binding:
window.addEventListener("load", function() {
// ...
initChallenge();
});
Or, you can use an IIFE with the inner binding:
(function () {
// ...
window.addEventListener("load", initChallenge);
})();

Convert Onclick to addEventListener

for(var i=1;i<=s;i++){if(e[r]<0&&n<=0)
{n=Math.abs(e[r])-1;r++}
else if(n>0){n--}else{t=e[r];r++}
var o=document.createElement("div");
o.style.height=t+"px";o.className="thumbnail";
o.id="thumb"+i;
o.setAttribute("onclick","Viewer.goToPage("+i+");");
I'm trying to convert onclick into addEventListener due to CSP restrictions in Firefox OS but not getting success.
for (var i = 1; i <= s; i++) {
if (e[r] < 0 && n <= 0) {
n = Math.abs(e[r]) - 1;
r++
} else if (n > 0) {
n--
} else {
t = e[r];
r++
}
var o = document.createElement("div");
o.style.height = t + "px";
o.className = "thumbnail";
o.id = "thumb" + i;
(function(j, elem) {
elem.addEventListener('click', function() {
Viewer.goToPage(j);
}, false);
}(i, o));
}
You'll have to capture the value of the iterator in an IIFE to make it persist in the callback for addEventListener
You can use addEventListener for this, however I don't see any benefit over direct property assignment (based on adeneo's code):
o.onclick = (function(pageNum) {
return function() {
Viewer.goToPage(pageNum);
};
}(i));
// Presumably o is added to the document before the next iteration
One benefit of this approach is that you can remove the listener later by assigning a new value to the element's onclick property, or add additional listeners using addEventListener (or equivalent).
If addEventListener is used to add an "anonymous" function, there's no easy way to remove it later.
And it's good to avoid setAttribute for attaching handlers as it's inconsistent across browsers. Directly setting the property is supported by every browser back to IE and NN version 3 or earlier.

Javascript .click() different for elements w/ same class

I'm pretty new to Javascript/Jquery and am implementing a real simple image carousel for practice and ran into a problem regarding Jquery's "click" method.
The code I currently have is as follows:
$(document.getElementsByClassName("traverse")).click(function() {
if(this.id = "left"){
if (current == 0) {
current = images.length-1;
}
else {
current -= 1;
}
}
else if(this.id = "right") {
if(current = images.length-1) {
current = 0;
}
else {
current += 1;
}
}
$(document.getElementById("image-view")).css("background-image", "url(" + images[current] + ")");
});
With this code there are no errors, but every time I click either the "#right" or "#left" button, they both run code as if "this.id = 'left'". While I understand I can simply separate the two and this will work fine, is there a way I can do it similar to what I have now where I'm applying this event to the class, but differentiating the behavior by the id?
Thanks!
Typo
== to compare
= to assign
Strict equal (===) Returns true if the operands are equal and of the
same type
if(this.id === "left"){
^
and better use
class selector
$('.traverse').click(function(){ .. });
Problem with your code
you are assign this.id = "left" every time in if condition so is condition is always true
if(this.id = "left"){
You're setting this.id to left by only using a single = sign. Try this:
$(document.getElementsByClassName("traverse")).click(function(event) {
if(event.target.id === "left"){
and so on.

How can I detect a jquery Fade animation is in process or not?

Here is the code about jQuery fadeIn fadeOut
function timecheck(obj,options,inAction){
var items = options.items;
// check if anything needs to be done
for (var i=0; i<items.length; i++) {
if(items[i][0]> obj.get(0).currentTime) {
$('#item_'+i).hide();
}else if(items[i][0]<=obj.get(0).currentTime && items[i][1] >= obj.get(0).currentTime && !inAction['item_'+i]){
console.log(inAction['item_'+i] = true);
$('#item_'+i).fadeIn('normal',function(){ inAction['item_'+i] = false; console.log(inAction); });
}else if(!inAction['item_'+i]){
console.log(inAction['item_'+i] = true);
$('#item_'+i).fadeOut('normal',function(){ inAction['item_'+i] = false; console.log(inAction); });
}
}
This code call by javascript every 40ms. my problem is callbacks try to set false incorrect inAction elements. How can i provide that?
For loops continue increasing. You need a closure and since you're using jQuery already, here is an option:
function timecheck(obj, options, inAction) {
var items = options.items;
// check if anything needs to be done
$.each(items, function(i) {
if (items[i][0] > obj.get(0).currentTime) {
$('#item_' + i).hide();
} else if (items[i][0] <= obj.get(0).currentTime && items[i][1] >= obj.get(0).currentTime && !inAction['item_' + i]) {
console.log(inAction['item_' + i] = true);
$('#item_' + i).fadeIn('normal', function () {
inAction['item_' + i] = false;
console.log(inAction);
});
} else if (!inAction['item_' + i]) {
console.log(inAction['item_' + i] = true);
$('#item_' + i).fadeOut('normal', function () {
inAction['item_' + i] = false;
console.log(inAction);
});
}
});
}
Otherwise, what is happening is that when the callback is called, it's already at the i already equals items.length
You can use the jQuery animation queues. Simply pass a string representing the name of your animation queue to the animating functions (hide, fadeIn, fadeOut, ...). For example, use your hide function like :
$('#item_'+i).hide({queue: "queueName"});
Then simply retrieve the number of animations left in your animation queue using :
$('.#item_'+i).queue("queueName").length;
You can intermittently check the value of length using window.setInterval and then apply your logic when the length property reaches 0.
See jQuery documentation for details :
queue
animate (same thing for hide(), fadeIn() and fadeOut())

Categories

Resources