So, i have started building a game 2d just a few day so its make me so confuse with some reason of the movement character in the tilemap.In my tilemap, every per tiles2 is have the width and height 16px and the whole map is 800 x 1250, every movement of my character will add/sub 1 in per step, when I calculating detection collisions, the position became wrong position specifically when character move a step then the position return to far though i try to divide 16 px in every movement of character.
This is my movement:
this.setting_walking_speed = 3 ;
if (this.player.control_direction[0] == 1) {
this.player.x -= this.setting_walking_speed;
this.player.walking = 1;
this.player.direction = 0;
} else if (this.player.control_direction[1] == 1) {
this.player.y -= this.setting_walking_speed;
this.player.walking = 1;
this.player.direction = 2;
} else if (this.player.control_direction[2] == 1) {
this.player.x += this.setting_walking_speed;
this.player.walking = 1;
this.player.direction = 1;
} else if (this.player.control_direction[3] == 1) {
this.player.y += this.setting_walking_speed;
this.player.walking = 1;
this.player.direction = 3;
}
var posf = {
x:Math.floor( this.player.y /16 ) ,
y:Math.floor( this.player.x /16 )
};
this.collide(posf);
console.log(posf.x , posf.y);
}
And the collision function is:
The this.setting_minblocksize\[k\] = 16;
this.bound = this.draw_collision();
function rectangularCollision({rect1, rect2 }) {
return (
rect1.x >= rect2.x &&
rect1.x <= rect2.x &&
rect1.y <= rect2.y &&
rect1.y >= rect2.y
)
}
this.collide = function(player){
// console.log(this.bound);
this.bound.forEach((boundaries) => {
if(
rectangularCollision({,
rect1: player,
rect2: boundaries
})
){
console.log('collide');
}
})
}
this.draw_collision = function () {
var obj = [];
this.ctxt.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < canvas.height; i++) {
for (var j = 0; j < canvas.width; j++) {
var data = 0;
if (i >= 0 && j >= 0 && i < this.map.layers[1].height && j < this.map.layers[1].width) {
var data = this.map.layers[1].data[i * this.map.layers[1].width + j];
for (var k = 0; k < 1; k++) {
this.ctxt.fillStyle = 'red';
if (data != 0) {
obj.push({
x: j ,
y: i
});
this.ctxt.fillRect(
(j * this.setting_minblocksize[k]) ,
(i * this.setting_minblocksize[k]) ,
this.setting_minblocksize[k],
this.setting_minblocksize[k]
);
}
}
}
}
}
return obj;
}
My codepen link is right here
Thank you all for helping me!
In javascript, I'm making an HTML canvas game, and in that game I have an object type/constructor called gamePiece. gamePiece has a function called checkCollision:
this.checkCollision = function(piece){
var collisionX = piece.x >= this.x && piece.x <= (this.x + this.width);
var collisionY = piece.y <= this.y && piece.y <= (this.y - this.height);
if(collisionX || collisionY){
return true;
} else {
return false;
}
}
which is called by update()
function update(){
context.clearRect(0, 0, game.width, game.height);
for(var i = 0; i < gamePieces.length; i++){
gamePieces[i].update();
for(var mi = 0; mi < gamePieces.length; mi++){
gamePieces[i].checkCollision(gamePieces[mi]);
if(gamePieces[i].checkCollision(gamePieces[mi]) == true){
gamePieces[i].collisionFunction();
}
}
}
}
setInterval(function(){update();}, 1);
I have another object that is supposed to give a speed boost upon colliding with another game piece, and it logs every time it gives a speed boost.
var speedBooster = new gamePiece(25,25,"red",300,300,0);
speedBooster.collisionFunction = function(){
for(var whichpiece = 0; whichpiece < gamePieces.length; whichpiece++){
if(speedBooster.checkCollision(gamePieces[whichpiece]) == true && gamePieces[whichpiece] != this){
gamePieces[whichpiece].speed += 10;
console.log("gamePieces[" + whichpiece + "] has been given a speed boost.");
}
}
}
But it gives a speed boost whenever a piece is behind it, and I put the "piece.x >= this.x &&" there for a reason. Why is JavaScript ignoring the condition I gave it?
Try
var collisionX = piece.x >= this.x && piece.x <= (this.x + this.width);
var collisionY = piece.y >= this.y && piece.y <= (this.y + this.height);
if(collisionX && collisionY){
return true;
} else {
return false;
}
To test if two objects overlap. Where the object has x,y as the top left and w,h as width and height
//Returns true if any part of box1 touches box2
function areaTouching(box1,box2){
return ! (box1.x > box2.x + box2.w ||
box1.x + box1.w < box2.x ||
box1.y > box2.y + box2.h ||
box1.y + box1.h < box2.y)
}
I have written a code bellow for counting the character inside text box.
the code is working just fine the only problem with it is when i past a text into the text box i have to press any key so system start to count.
Could you please help me sort this problem
function GetAlhpa(text) {
var gsm = "#£$¥èéùìòÇØøÅåΔ_ΦΓΛΩΠΨΣΘΞ^{}\[~]|€ÆæßÉ!\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";
var i = 0;
while (i <= String(text).length) {
if (gsm.indexOf(String(String(text).charAt(i))) == -1 && (String(text).charCodeAt(i) != 32) && (String(text).charCodeAt(i) != 27) && (String(text).charCodeAt(i) != 10) && (String(text).charCodeAt(i) != 13)) {
UniCodestring = " Unicode ";
Countsms = 70;
if ($('#SndSms_Message').val().length > 70)
Countsms = 67;
return;
}
i++;
}
Countsms = 160;
UniCodestring = "";
if ($('#SndSms_Message').val().length > 160)
Countsms = 153;
}
var Countsms = 160;
var UniCodestring = "";
var CounterSmsLen = 0;
var Two = "|^€{}[]~";
function GetCountSms() {
document.getElementById('SndSms_Message').addEventListener('input', function (e) {
var target = e.SndSms_Message,
position = SndSms_Message.selectionStart;
ConvertGreek();
CounterSmsLen = $('#SndSms_Message').val().length;
GetAlhpa($('#SndSms_Message').val());
var i = 0;
while (i < String(Two).length) {
var oldindex = -1;
while (String($('#SndSms_Message').val()).indexOf(String(String(Two).charAt(i)), oldindex) > -1) {
//if ( String($('#SndSms_Message').val()).indexOf(String(String(Two).charAt(i))) > -1){
CounterSmsLen += 1;
oldindex = String($('#SndSms_Message').val()).indexOf(String(String(Two).charAt(i)), oldindex) + 1;
console.log(i);
}
i++;
}
SndSms_Message.selectionEnd = position; // Set the cursor back to the initial position.
});
if ($('#SndSms_Message').val().length == 0)
CounterSmsLen = 0;
$('#SndSms_Count').html(' ' + CounterSmsLen + ' Characters' + UniCodestring + ' <br /> ' + Math.ceil(CounterSmsLen / Countsms) + ' Sms');
countsmsnumber=Math.ceil(CounterSmsLen / Countsms);
}
var greekchar = "ΑΒΕΖΗΙΚΜΝΟΡΤΥΧ";
var englishchar = "ABEZHIKMNOPTYX";
function ConvertGreek() {
var str = $('#SndSms_Message').val();
var i = 0;
while (i < String(greekchar).length) {
str = str.replace(new RegExp(String(greekchar).charAt(i), 'g'), String(englishchar).charAt(i));
i++;
}
$('#SndSms_Message').val(str);
P.S.
If i paste the number into the text box it will count it correct but if i paste character it wont count them..
You need keyup change event in order to handle paste event.
document.getElementById('SndSms_Message').addEventListener("keyup", function() {
//your code here
});
example
I want to create a drawing in sugar thing, much the same as you would throw some sugar on a table and using your fingers to "erase" the sugar particles to form an image.
Does anyone know of a js type tool I can use to make this happen?
I suppose I can take a photo of a desk, and a photo of desk with some sugar on it, and then just erase the top layer, but I'm worried that this won't give a real effect.
I'm currently thinking of having a photo of desk, and then using JS to generate a lot of "sugary" particles, which I can then erase. This sounds incredibly hard to do though. Is it? Can someone point me in a good direction?
Sand or is it sugar?
An interesting problem that I had to give a little time.
This works by creating several buffers to hold grains of sand (sugar) and give them life when they need to move.
There is no way that Javascript could do a whole screen of a million plus grains so this demo cheats by only updating a very few and prioritising for new movement rather than allow older moving grains to hog CPU time.
The arrays active, sandStatus, holds the sand gains. active has the pixel address as a 32Bit int and sandStatus has age. The Array sand holds the amount of sand at each pixel and is used to calculate the shadow effect (shadow could be much better using a webGL shader) and to work out which direction sand should slide if disturbed or dropped to the surface.
the var activeMax holds the max number of active sand grains. Increase for a better effect, decrease if the sim runs to slow.
To drop sand use the right mouse button. Hold at one spot to make a pile. Left button pushes the sand about. When you hit a bigger pile the machine may lag (depending on CPU power and browser (best in firefox)).
The push function checks the sand array for any sand. If found it pushes the sand away from the center and piles it up around the edge. Some sand will fall back.
The function sprinkle adds grains of sand (one are a time by pixel coordinate or by index). The function push does the sand drawing FX. update moves the sand grains by checking surrounding pixels heights and moving grains down hill. renderPix handles rendering grains, creating the shadows and deactivating sand grains. The Array shadowChange holds the index of pixels that have had changes so that the shadows can be updated.
Bottom half of the demo is just boilerplate for mouse and canvas setup. All the code in regard to the answer is in the first half.
"use strict";
var activeMax = 2280; // this is the number of sand grains that are processed at
// at time. Increase for better looking effect. decrease
// if the machine is not keeping up with the load
var cw;
var ch;
var w; //
var h;
var canvasBuf = document.createElement("canvas");
var ctxB
var globalTime; // global to this
var pixels
var sand;
var sandToFall;
var sandToFallCount = 36000;
var shadow; // shadow pixels
var activeMax = 2280;
var active; // index of pixel for active grain
var sandStatus; // status of active grain
var shadowChange; // holds index of pixels that have a shadow change
var pixels;
var buf;
var grain = 0xFFFFFFFF;
var shadowGrain = 0x00000000;
var ready = false;
var sandReady = 0;
var nextActive = 0;
var nextActiveShadow = 0;
var onResize = function(){
cw = canvas.width;
ch = canvas.height;
w = cw; //
h = ch;
pixels = w*h;
canvasBuf.width = w;
canvasBuf.height = h;
ctxB = canvasBuf.getContext("2d");
sand = new Uint8ClampedArray(pixels);
shadow = new Uint8ClampedArray(pixels); // shadow pixels
sandToFall = new Uint32Array(sandToFallCount);
activeMax = 2280;
active = new Uint32Array(activeMax); // index of pixel for active grain
sandStatus = new Uint16Array(activeMax); // status of active grain
shadowChange= new Uint32Array(activeMax); // holds index of pixels that have a shadow change
sandStatus.fill(0); // clear
active.fill(0);
shadowChange.fill(0);
ctxB.clearRect(0,0,w,h);
ctxB.fillStyle = "white";
ctxB.font = "84px arial";
ctxB.textAlign = "center";
ctxB.globalAlpha = 0.01;
for(var i = 0; i < 12; i ++){
ctxB.fillText("Sand Doodler!",w/2 + (Math.random()-0.5)*5,h/2 + (Math.random()-0.5)*5);
}
ctxB.globalAlpha = 1;
pixels = ctxB.getImageData(0,0,w,h);
buf = new Uint32Array(pixels.data.buffer);
for(i = 0; i < buf.length; i += 3){
if(buf[i] !== 0){
var c = buf[i] >>> 24;
buf[i] = 0;
while(c > 0){
var ind = Math.floor(Math.random()*sandToFallCount);
if(sandToFall[ind] === 0){
sandToFall[ind] = i;
}
c = c >>> 1;
}
}
}
buf.fill(0);
offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
shadowOffsets = [-w-1,-w,-1];
ready = true;
sandReady=0;
}
function sprinkle(x,y){
var ind;
if(y === undefined){
ind = x;
}else{
ind = x + y*w;
}
var alreadyExists = active.indexOf(ind);
var ac = nextActive;
if(alreadyExists > -1){
sand[ind] += 1;
shadow[ind] = 0;
sandStatus[alreadyExists] = 66;
}else{
active[nextActive] = ind;
sandStatus[nextActive] = 66;
shadowChange[nextActiveShadow];
nextActiveShadow = (nextActiveShadow+1)%activeMax;
nextActive = (nextActive +1)%activeMax;
sand[ind] += 1;
shadow[ind] = 0;
}
return ac;
}
var offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
var offsetCount = 8;
function update(){
var min,max,minDir,maxDir,dir,start,jj,j,ind,level,i,l1;
for( i = 0; i <activeMax; i ++){
if(sandStatus[i] !== 0){
ind = active[i];
level = sand[ind];
if(level === 1){
sandStatus[i] = 1; // deactive is cant move (level ground)
}else{
min = level;
var d;
minDir = offsets[Math.floor(Math.random()*16)];
dir = null;
start = Math.floor(Math.random()*16); // start at a random direction
for(j=0;j < offsetCount; j++){
jj = offsets[(j + start)%offsetCount];
l1 = sand[ind+jj];
if(l1 < min){
min = l1;
minDir = jj;
d = (j + start)%offsetCount;
}
}
dir = null;
if(min >= level - 1){ // nowhere to move
sandStatus[i] = 1;
}else
if(min < level-1){ // move to lowest
dir = minDir
}
if(dir !== null){
var lv = level-min;
while(lv > 2){
active[i] = ind + dir;
if(sand[ind] > 1){
sand[ind] -= 2;
sprinkle(ind)
}else{
sand[ind] -=1;
}
ind = ind+dir;
sand[active[i]] += 1;
if(sand[active[i] + offsets[d]] >=level){
d+= Math.random()<0.5? 1 : offsetCount -1;
d %=offsetCount;
}
lv -= 1;
}
if(sand[ind]>0){
active[i] = ind + dir;
sand[ind] -= 1;
}
sand[active[i]] += 1;
}
}
}
}
}
var shadowOffsets = [-w-1,-w,-1];
var shadowCols = [0xFFf0f0f0,0xFFd0d0d0,0xFFb0b0b0,0xFF909090];
var shadowDist = [0xf0000000,0xd0000000,0xb0000000,0x90000000]; // shadow col no sand
// renders grains and adds shadows. Deactivates gains when they are done
function renderPix(){
var ac = 0;
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
ac += 1;
var ind = active[i];
buf[ind] = grain;
}
}
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
var ind = active[i];
var level = sand[ind];
var col =0;
if(sand[ind + shadowOffsets[0]] > level ){
col = 2;
}else
if(sand[ind + shadowOffsets[1]] > level ){
col =1;
}else
if(sand[ind + shadowOffsets[2]] > level ){
col = 1;
}
buf[ind] = grain; // add a sand grain to the image
shadow[ind] = col;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
var c = 4;
while(c > 0){
c-=1;
ind += w + 1;
var s = sand[ind];
var dif = level - s;
if(dif > 0){
c-= dif;
}
shadow[ind] += 1;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
}
sandStatus[i] -= 1;
if(sandStatus[i] === 1){
sandStatus[i] = 0;
active[i] = 0;
}
}
}
// add calculated shadows
for(var i = 0; i < activeMax; i ++){
if(shadowChange[i] !== 0){
var ind = shadowChange[i];
var s = shadow[ind] <4 ? shadow[ind]-1:3;
if(sand[ind] > 0){
buf[ind]=shadowCols[s];
}else{
buf[ind]=shadowDist[s];
}
shadowChange[i] = 0;
}
}
}
// push sand about
function push(x,y,radius){
var iyy,iny
var rr = radius * radius ;
x = Math.floor(x);
y = Math.floor(y);
for(var iy = -radius + 1; iy < radius; iy ++){
iyy = iy * iy;
iny = (y+iy) * w;
for(var ix = -radius + 1; ix < radius; ix ++){
if(ix*ix + iyy <= rr){ // is inside radius
var ind = (x + ix) + iny;
if(sand[ind] > 0){
var dir = Math.random() * Math.PI * 2;
dir = Math.atan2(iy,ix)
var r = radius + Math.random() * radius *0.2
var xx = Math.cos(dir) * r;
var yy = Math.sin(dir) * r;
buf[ind] = 0x000000;
sand[ind] = 0;
ind = Math.floor(xx + x) + Math.floor(yy + y) * w;
sprinkle(ind);
}else{
buf[ind] = 0;
}
}
}
}
}
function showHeight(){ // for debugging only
for(var i = 0; i < sand.length; i ++){
buf[i] = 0xff000000;
var k = sand[i];
buf[i] +=(k <<16) + (k<<8) + (k);
}
}
// main update function
function display(){
if(!ready){ // only when ready
return;
}
//ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,cw,ch);
var mx = Math.floor((mouse.x/cw)*w); // canvas buf mouse pos
var my = Math.floor((mouse.y/ch)*h);
// drop sand
if(mouse.buttonRaw & 4){
for(var i = 0; i < 120; i ++){
var dir = Math.random()*Math.PI;
var dist = ((Math.random()+Math.random()+Math.random())/3-0.5) * 62;
var x = Math.cos(dir) * dist;
var y = Math.sin(dir) * dist;
x += mx;
y += my;
x = Math.floor(x); // floor
y = Math.floor(y); // floor
sprinkle(x,y);
}
}else{
// drop sand for intro FX
if(sandReady <sandToFallCount){
for(var i = 0; i < 120; i ++){
if(sandToFall[sandReady] !== 0){
sprinkle(sandToFall[sandReady] + offsets[Math.floor(Math.random()*8)%offsets.length]);
}
sandReady += 1;
}
}
}
// push sand about.
if(mouse.buttonRaw & 1){
push(((mouse.x/cw)*w),((mouse.y/ch)*h),32); // scale mouse to canvasBuf size
}
update();
renderPix();
//showHeight();
ctxB.putImageData(pixels,0,0);
ctx.drawImage(canvasBuf,0,0,cw,ch);
}
//==================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// This code is not intended to be part of the answer unless specified and has been formated to reduce
// display size. It should not be used as an example of how to write a canvas interface.
// By Blindman67
const U = undefined;const RESIZE_DEBOUNCE_TIME = 100;
var canvas,ctx,mouse,createCanvas,resizeCanvas,setGlobals,globalTime=0,resizeCount = 0;
createCanvas = function () { var c,cs; cs = (c = document.createElement("canvas")).style; cs.position = "absolute"; cs.top = cs.left = "0px"; cs.zIndex = 1000; document.body.appendChild(c); return c;}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); } canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); } if (typeof onResize === "function"){ resizeCount += 1; setTimeout(debounceResize,RESIZE_DEBOUNCE_TIME);}
}
function debounceResize(){ resizeCount -= 1; if(resizeCount <= 0){ onResize();}}
setGlobals = function(){ cw = w = canvas.width; ch = h = canvas.height; mouse.updateBounds(); }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, over : false, bm : [1, 2, 4, 6, 5, 3],
active : false,bounds : null, crashRecover : null, mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.clientX - m.bounds.left; m.y = e.clientY - m.bounds.top;
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
if((m.buttonRaw & 2) && m.crashRecover !== null){ if(typeof m.crashRecover === "function"){ setTimeout(m.crashRecover,0);}}
e.preventDefault();
}
m.updateBounds = function(){
if(m.active){
m.bounds = m.element.getBoundingClientRect();
}
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
m.active = true;
m.updateBounds();
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
m.active = false;
}
}
return mouse;
})();
function main(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(main);
}
resizeCanvas();
mouse.start(canvas,true);
window.addEventListener("resize",resizeCanvas);
requestAnimationFrame(main);
body {
background:#49D;
}
.help {
text-align : center;
font-family : Arial,"Helvetica Neue",Helvetica,sans-serif;
font-size : 18px;
}
<div class="help">Right mouse to drop sand, left button to push it around.</div>
Is there a better way to do this to reduce code duplication?
Maybe somehow loop the 'pagesTop' array?
The following function is initialized on the windows scroll event.
Thanks.
function redrawSideNav() {
var pagesTop = new Array();
$('.page').each(function(index, elem){
pagesTop[index] = $(this);
});
$('.menu, .page').find('a').removeClass('active');
if ( $(document).scrollTop() >= pagesTop[0].offset().top && $(document).scrollTop() < pagesTop[1].offset().top) {
var target = '#' + pagesTop[0].attr('id');
$('[href^="'+target+'"]').addClass('active');
} else if ( $(document).scrollTop() >= pagesTop[1].offset().top && $(document).scrollTop() < pagesTop[2].offset().top) {
var target = '#' + pagesTop[1].attr('id');
$('[href^="'+target+'"]').addClass('active');
} else if ( $(document).scrollTop() >= pagesTop[2].offset().top && $(document).scrollTop() < pagesTop[3].offset().top) {
var target = '#' + pagesTop[2].attr('id');
$('[href^="'+target+'"]').addClass('active');
} else if ( $(document).scrollTop() >= pagesTop[3].offset().top && $(document).scrollTop() < pagesTop[4].offset().top) {
var target = '#' + pagesTop[3].attr('id');
$('[href^="'+target+'"]').addClass('active');
} else if ( $(document).scrollTop() >= pagesTop[4].offset().top) {
var target = '#' + pagesTop[4].attr('id');
$('[href^="'+target+'"]').addClass('active');
}
}
Well, it seems that only the number changes in the following code, so you can use a for-loop with a break-statement. When the break get's triggered, the rest of the for-loop will not be executed. The last else if(...) is the same, but without the last condition. We can fake this by appending a dummy-element to pagesTop, that makes the last condition always true.
var a = {
offset: function() {
return {'top': 9999999999999};
}
};
pagesTop.push( a );
for( var i = 0; i < pagesTop.length-1; i++ ) {
if ( $(document).scrollTop() >= pagesTop[i].offset().top && $(document).scrollTop() < pagesTop[i+1].offset().top) {
var target = '#' + pagesTop[i].attr('id');
$('[href^="'+target+'"]').addClass('active');
break;
}
}
Do it step by step:
First I would create a variable for $(document).scrollTop() and replace the code inside the if statements:
var scrollTop = $(document).scrollTop();
var targetIndex = null;
$('.menu, .page').find('a').removeClass('active');
if ( scrollTop >= pagesTop[0].offset().top && scrollTop < pagesTop[1].offset().top) {
targetIndex = 0
} else if ( scrollTop >= pagesTop[1].offset().top && scrollTop < pagesTop[2].offset().top) {
targetIndex = 1;
} else if ( scrollTop >= pagesTop[2].offset().top && scrollTop < pagesTop[3].offset().top) {
targetIndex = 2;
} else if ( scrollTop >= pagesTop[3].offset().top && scrollTop < pagesTop[4].offset().top) {
targetIndex = 3;
} else if ( scrollTop >= pagesTop[4].offset().top) {
targetIndex = 4;
}
var target = '#' + pagesTop[targetIndex].attr('id');
$('[href^="'+target+'"]').addClass('active');
After that I would start building a loop instead if of using else if cascades:
var scrollTop = $(document).scrollTop();
var targetIndex = null;
for (var i = 0; i < 4 && !targetindex; i++) {
if (scrollTop >= pagesTop[i].offset().top && scrollTop < pagesTop[i + 1].offset().top) {
targetIndex = i;
}
}
targetIndex = targetIndex || 4;
var target = '#' + pagesTop[targetIndex].attr('id');
$('[href^="'+target+'"]').addClass('active');
(untested.. maybe I made some indices/syntax errors..)
the code below works for dynamic sized arrays:
var scrollTop = $(document).scrollTop();
for (var i=0;i<pagesTop.length ;i++) {
tempCond = pagesTop[i+1] ? (scrollTop <= pagesTop[i+1].offset().top) : true;
if(scrollTop >= pagesTop[i].offset().top && tempCond ) {
var target = '#' + pagesTop[i].attr('id');
$('[href^="'+target+'"]').addClass('active');
break;
}
}
difference with samurai answer:
rather than adding a fictional item to the array to guarantee the loop is executed at least one time and the second condition of if statement is always true,
I check inside the loop if the current element is the last item in the array: if it is , the second condition in the if statement is directly set to true, else, the condition is set to scrollTop <= pagesTop[i+1].offset().top