Simple pure javascript image slider - javascript

I'm trying to make the most simple image slider possibly in pure js, basically by changing the background position, but loop doesn't work at all. I need to repeat the loop continuously.
function slider1(){
var slide = document.getElementById("slider-1");
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
var f;
for(f=backPosX;f < sliderEnd; f+=slideWidth){
slide.style.backgroundPosition = "-"+f+"px "+backPosY+"px";
}
setInterval(slider1(),1000);}

setInterval takes function as first parameter, but you passed slider1().
Parentheses means calling function so you are calling slider1 and passing to setInterval only returned value of function call.
In your case slider1 does not return anything, that means that setInterval receives undefined instead of your function.
You need to pass the function itself, not results of its calling.
setInterval(slider1, 1000);
But be aware that every call will start a new interval and you don't want to have multiple intervals running calling the same function.
So call setInterval outside of your function or replace setInterval with setTimeout. Timeout will call your function once and your function will create new timeout and so on.
Edit:
Also your for loop loops through all positions in one moment. But you want to move it by one position.
I would do it like this:
var slide = document.getElementById("slider-1");
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
var f = backPosX;
function slider1(){
slide.style.backgroundPosition = "-"+f+"px "+backPosY+"px";
f+=slideWidth;
if(f >= sliderEnd)
f = backPosX;
}
setInterval(slider1, 1000);
Edit 2: This code can be used on multiple sliders:
function moveSlider(slide){ // Slide is now a function parameter
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
if(!slide.slidePosition) // If there isn't slidePosition in the element
slide.slidePosition = backPosX; // Initialize it
slide.slidePosition+=slideWidth;
if(slide.slidePosition >= sliderEnd) //
slide.slidePosition = backPosX;
slide.style.backgroundPosition = "-"+slide.slidePosition+"px "+backPosY+"px";
}
function startSlider(slide){ // Start slider's interval
return setInterval(function(){
moveSlider(slide);
}, 1000);
}
function stopSlider(intervalId){ // setInterval return ID which you can use to stop the interval
clearInterval(intervalId);
}
You can then start slider with:
startSlider(document.getElementById("slider-1"));
Notice that I need to pass parameter to moveSlider. So I wrapped in another function doesn't take any parameters and calls moveSlider with slider as parameter. This code example can explain it a bit:
function slide1(){
...
}
// Can be also written as:
slide1 = function(){
...
}
I just removed the middle man slide1 and passed it directly.

Related

Using Javascript Varibles Without Calling Functions In Them

I am currently devolving a little slideshow, thing that automatically moves fades in and out the background-images. (opacity as well.)
My issue is that I am trying to use variables to store code to run, as setTimeout is stubborn, and won't run with anything in parentheses in it. (as well I need to use them, or my code will get really messy..ier)
What I currently have is
imgID = 0;
// window.setInterval(nextSLIDE, 1000);
nextSLIDE();
function nextSLIDE( step2 ) {
slideVAR = "slide" + imgID;
window.setTimeout(imgIDchange(), 50);
test2 = window.setTimeout(changeOpacityNINE);
tes5t = window.setTimeout(changeOpacity8, 100); // If you are wondering about the irradical names, that is because I made them all non-unique earlier, and I got lazy, so I made them unique..
test4 = window.setTimeout(changeOpacity7, 200);
test6 = window.setTimeout(changeOpacity6, 300);
tes6t = window.setTimeout(changeOpacity5, 400);
twest = window.setTimeout(changeOpacity4, 500);
testt = window.setTimeout(changeOpacity3, 600);
testyt = window.setTimeout(changeOpacity2, 700);
teswt = window.setTimeout(changeOpacity1, 800);
}
function imgIDchange() {
imgID = imgID + 1;
}
function changeOpacity( opacity ) {
document.getElementById("headerIMG").style.opacity = opacity;
}
var changeOpacityNINE = changeOpacity(0.9);
var changeOpacity8 = changeOpacity(0.8);
var changeOpacity7 = changeOpacity(0.7);
var changeOpacity6 = changeOpacity(0.6);
var changeOpacity5 = changeOpacity(0.5);
var changeOpacity4 = changeOpacity(0.4);
var changeOpacity3 = changeOpacity(0.3);
var changeOpacity2 = changeOpacity(0.2);
var changeOpacity1 = "changeOpacity(0.1);"
var FULL = changeOpacity(1)
And I am looking for a way to make it either
A) Work, and not run the varibles.
B) Or find some sort of other Work around..
If my design is that horrific, and makes your eyes bleed feel free to tell me how I can redo it, but I would rather not redo it all in general. (I am rather lazy..)
If I understand you correctly, you want to call setTimeout with a function and pass arguments to it?
If so, you can simply add the arguments to the end of your setTimeout call. So if you wanted to call
changeOpacity(0.5);
after 1000 ms, then you would use setTimeout like this:
setTimeout(changeOpacity, 1000, 0.5);
With setTimeout, the arguments are as follows:
setTimeout(callback,delay,args);
so you can simply do:
setTimeout(changeOpacity,*DELAY*,0.7); // etc...

How can I change this into a loop instead of a recursive function?

So I have a piece of code like
var barlen = $('#SSWEprogressbar').width(),
$elems = $('[data-srcurl]'),
k = 0,
n = $elems.length;
LoadImage();
function LoadImage()
{
var $elem = $($elems[k]);
var img = new Image(),
url = $elem.attr('data-srcurl');
$(img).load(function(){
$('#SSWEloaderfront').attr('src',url);
$('#SSWEloadprogress').width((k+1)/n*barlen + "px");
var srctgt = $elem.attr('data-srctgt');
// change url to src attribute or background image of element
if ( srctgt == "srcattr" ){ $elem.attr('src',url); }
else if ( srctgt == "bgimg" ) { $elem.css('background-image',"url("+url+")"); }
// decide whether to exit the
if ( ++k == n ) { AllyticsSSWEPlayerShow(); }
else { LoadImage(); }
});
img.src = url;
}
and the reason I have it written that way is because load callback needs to be called before the stuff in the function can be executed again. If possible, I'd like to change this from a recursive function to a loop, but I don't know how to do that because there's no way to make a for or while loop "wait" before going on to the next iteration. Or is there?
As I mentioned in the comment you can easily resolve your problem, by using setTimeout(LoadImage, 100); in the else instead of calling the function directly. The 2nd parameter is the delay in ms.
If you understand why setTimeout(LoadImage, 0); is not stupid and not the same as calling the function directly then you understood setTimeout. It puts the function call in the queue, this means other events like clicks or keys that were pressed can be processed before the function is called again and the screen doesn't freeze. It's also impossible to reach max recursion like this, the depth is 1.

naturalWidth and naturalHeight returns 0 using onload event

I have read countless of answers of this issue and I came up with the following, but it doesn't work either.
function fitToParent(objsParent, tagName) {
var parent, imgs, imgsCant, a, loadImg;
//Select images
parent = document.getElementById(objsParent);
imgs = parent.getElementsByTagName(tagName);
imgsCant = imgs.length;
function scaleImgs(a) {
"use strict";
var w, h, ratioI, wP, hP, ratioP, imgsParent;
//Get image dimensions
w = imgs[a].naturalWidth;
h = imgs[a].naturalHeight;
ratioI = w / h;
//Get parent dimensions
imgsParent = imgs[a].parentNode;
wP = imgsParent.clientWidth;
hP = imgsParent.clientHeight;
ratioP = wP / hP;
//I left this as a test, all this returns 0 and false, and they shouldn't be
console.log(w);
console.log(h);
console.log(ratioI);
console.log(imgs[a].complete);
if (ratioP > ratioI) {
imgs[a].style.width = "100%";
} else {
imgs[a].style.height = "100%";
}
}
//Loop through images and resize them
var imgCache = [];
for (a = 0; a < imgsCant; a += 1) {
imgCache[a] = new Image();
imgCache[a].onload = function () {
scaleImgs(a);
//Another test, this returns empty, for some reason the function fires before aplying a src to imgCache
console.log(imgCache[a].src);
}(a);
imgCache[a].src = imgs[a].getAttribute('src');
}
}
fitToParent("noticias", "img");
To summarise, the problem is the event onload triggers before the images are loaded (or that is how I understand it).
Another things to add:
I don't know at first the dimensions of the parent nor the child,
because they varied depending of their position on the page.
I don't want to use jQuery.
I tried with another function, changing the onload event to
window, and it worked, but it takes a lot of time to resize because
it waits for everything to load, making the page appear slower,
that's how I came to the conclusion the problem has something to do
with the onload event.
EDIT:
I made a fiddle, easier to look at the problem this way
https://jsfiddle.net/whn5cycf/
for some reason the function fires before aplying a src to imgCache
Well, the reason is that you are calling the function immedeatly:
imgCache[a].onload = function () {
}(a);
// ^^^ calls the function
You call the function and assign undefined (the return value of that function) to .onload.
If you want to use an IIFE to capture the current value of a, you have to make it return a function and accept a parameter to which the current value of a is assigned to:
imgCache[a].onload = function (a) {
return function() {
scaleImgs(a);
};
}(a);
Have a look again at JavaScript closure inside loops – simple practical example .

Loop object method

i'm trying to call an objects methods at the same time with a loop.
I would create a game with a group of enemies that are move at the same time.
I create an object "Warrior"
function Warrior(x, y) {
// Coordinates x, y
this.x = x;
this.y = y;
this.character = $('<div class="enemy"></div>');
// Inserting the div inside #map
this.character.appendTo('#map');
// assigning x and y parameters
this.character.css({
marginLeft: x + 'px',
marginTop: y + 'px'
});
// walk method that move the div
this.walk = function() {
// Moving div 10px to the right
this.character.css('left', "+=10");
}
}
var enemies = new Array();
enemies[0] = new Warrior(0, 0);
enemies[1] = new Warrior(10, 20);
enemies[2] = new Warrior(60, 80);
for(var i=0;i<enemies.length;i++) {
setInterval(function(){enemies[i].walk()}, 1000);
}
I've created an array "enemies"
and I tried to call walk() methods at the same time
But nothing it happens. I would like that the divs are moving at the same time!
Thanks!
Based on this question
You might want to pass a parameter by value to setInterval:
for(var i=0;i<enemies.length;i++) {
setInterval(
function(index){ //<-- accept parameter here
enemies[index].walk();
}, 1000, i); //<-- pass i here
}
If you do not pass the parameter, x variable changes while loop is running, thus all the functions will be called having x = 2 (because you have 3 elements in your array)
You have two major problems where:
CSS does not allow programmatic modification in the way that javascript does. You modify values in javascript, and then set them in CSS:
this.walk = function() {
var current = parseInt(this.character.css('left')),
stepped = current + 10;
this.character.css('left', stepped);
}
Your loop creates a closure on var i which is only declared once, so the same value will be shared across the functions. You can either have the function take a parameter, and provide it in the setInterval call:
setInterval(function(index) { /* ... */}, 1000, i);
or you can put the loop within the interval function:
setInterval(function() {
for (var i = 0; i < enemies.length; i++) {
enemies[i].walk();
}
});
You might also want to reconsider whether your walk function should modify the CSS directly. You've already forgotten about your x and y coordinate attributes in this code. Perhaps you should have a walk method that modifies these coordinates, and a draw function that updates the sprites' positions based on their current coordinates.

onmouseout not firing properly

I have this code which is supposed to fire on mouseover and it's counterpart to do the opposite on onmouseout:
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number+8;
if(number<=184)
setTimeout(colinc,50);
}
The counter part only has the change of number = number-8; and number>=40;
The problem is i have multiple boxes that should light up with color change on mouseover and lightdown with mouseout. when i move slowly over my boxes(large in no.) then everything is ok but when i move quickly some boxes do not light down...it looks like the onmouseout doesn't happen if i pass very quickly.
Any help?
function flash(x){
number=0;
var cc = document.getElementById("c"+x);
var cs=document.defaultView.getComputedStyle(cc,null);
var bg=cs.getPropertyValue('background-color');
var str=""+bg;
var n=str.replace("rgb","");
n=n.replace("(","");
n=n.replace(")","");
var arr=n.split(",");
number=parseInt(arr[0]);
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number+8;
if(number<=184)
setTimeout(colinc,50);
}
}
function flashe(x){
number=0;
var cc = document.getElementById("c"+x);
var cs=document.defaultView.getComputedStyle(cc,null);
var bg=cs.getPropertyValue('background-color');
var str=""+bg;
var n=str.replace("rgb","");
n=n.replace("(","");
n=n.replace(")","");
var arr=n.split(",");
number=parseInt(arr[0]);
colinc();
function colinc(){
var hexnum=number.toString(16);
var hexcolor="#"+hexnum+hexnum+hexnum;
document.getElementById("c"+x).style.backgroundColor=hexcolor;
number=number-8;
if(number>=40)
setTimeout(colinc,40);
}
}
This is my full js code
Check whether the events fire properly by logging them in the console:
function MouseOverHandler(event) {
console.log('mouseover');
}
function MouseOutHandler(event) {
console.log('mouseout');
}
Also do you ever halt the execution of either handlers when the opposite event happens. This would be done via getting the timeout id and canceling it.
var mouseOverTimeout, mouseOutTimeout;
function colinc(){
clearTimeout(mouseOutTimeout);
mouseOverTimeout = setTimeout(colinc,50);
}
function MouseOutHandler(event) {
clearTimeout(mouseOverTimeout);
mouseOutTimeout = setTimeout(MouseOutHandler,50);
}
In your code:
> function colinc(){
>
> var hexnum=number.toString(16);
The identifier number hasn't be declared or initialised, so you get a reference error and the script fails. Before the above line, you should probably add:
var number = 0;
or give number some other value.
> var hexcolor="#"+hexnum+hexnum+hexnum;
> document.getElementById("c"+x).style.backgroundColor=hexcolor;
> number=number+8;
> if(number<=184)
> setTimeout(colinc,50);
But here you need access to a global number, so you can keep a reference in a closure or make number global. If you're going to do that, give it a better name, like *colnic_counter* or something that is unlikely to clash with some other global.
> }
Something like:
var colinc = (function() {
var num = 0;
return function() {
var hexnum = num.toString(16);
var hexcolor = "#" + hexnum + hexnum + hexnum;
// document.getElementById("c"+x).style.backgroundColor=hexcolor;
console.log(hexcolor);
num += 8;
if (num <= 184)
setTimeout(colinc,50);
}
}());
colinc();
Note that since a function expression is used to initialise the function, you have to call it afterward.
I have solved the problem of cleartimeout. I created two arrays to hold the current mouseover and mouseout setTimeout ids of every box according to their Id. Everytime a mouseout is called it first clears its corresponding mouseover from the array and same for mouseout.

Categories

Resources