Userscript working in Chrome but not in Firefox - javascript

This script searches for and grabs the closest element in a collection and scrolls to it depending on what key you press. It is set to scroll to the next and previous post_block elements on IP.Board forums. It also jumps through page with other keys.
It works exactly as expected in Chrome, but the scrolling doesn't work in Firefox. The page jumping does. It does not throw any errors, it just doesn't work.
Relevant code:
// Questionable functions
function getPosition(element) {
var xPosition = 0,
yPosition = 0;
while (element) {
xPosition += (element.offsetLeft
+ element.clientLeft);
yPosition += (element.offsetTop
+ element.clientTop);
element = element.offsetParent;
}
return {x: xPosition, y: yPosition};
}
Math.easeInOutQuad = function (time, start, change, duration) {
time /= duration / 2;
if (time < 1) {
return change / 2 * time * time + start;
}
time--;
return -change / 2 * (time * (time - 2) - 1) + start;
};
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 1;
var animateScroll = function() {
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
currentTime += increment;
if (currentTime <= duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
function scrollToNext(context, collection, dir) {
var item,
i = 0;
switch (dir) {
case 'up':
for (i = collection.length - 1; i >= 0; i--) {
item = collection[i];
if (getPosition(item).y < context.scrollTop - 2) {
scrollTo(context, getPosition(item).y, 30);
break;
}
}
break;
case 'down':
default:
for (i = 0; i < collection.length; i++) {
item = collection[i];
if (getPosition(item).y > context.scrollTop + 2) {
scrollTo(context, getPosition(item).y, 30);
break;
}
}
break;
}
}
// Trigger in keydown handler
if (event.keyCode === shortcuts['next_post'] && checkModifiers('next_post', event, shortcuts)) {
event.preventDefault();
scrollToNext(document.body, document.getElementsByClassName('post_block'), 'down');
} else if (event.keyCode === shortcuts['previous_post'] && checkModifiers('previous_post', event, shortcuts)) {
event.preventDefault();
scrollToNext(document.body, document.getElementsByClassName('post_block'), 'up');
}
Any ideas on what's wrong?

Found it. Firefox uses document.documentElement for its scrolling while Chrome uses document.body.
I had to use both to get it to work in all browsers.

Related

Customised scroll isnt working on smartphones

i have an issue concerning a JS scrolling function.
const scroll = document.querySelectorAll(".scroll");
const maxIndex = 3; //NB DE PAGES
let index = 0;
let animationEnd = true;
let boolUp = 0;
const start = {
x: 0,
y: 0
};
function touchStart(event) {
event.preventDefault();
start.x = event.touches[0].pageX;
start.y = event.touches[0].pageY;
}
function touchMove(event) {
event.preventDefault();
const offset = {};
offset.x = start.x - event.touches[0].pageX;
offset.y = start.y - event.touches[0].pageY;
scrollHandler({
deltaY: offset.y
});
}
function scrollHandler(e) {
if(e.preventDefault) e.preventDefault();
if (animationEnd) {
if (e.deltaY > 0) index++;
else index--;
if (index < 0) index = 0;
if (index > scroll.length - 1) index = scroll.length - 1;
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450)
}
}
function keyScroll(e) {
if (e.key == "ArrowUp") {
index--;
if (index < 0) {
index = 0;
}
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450);
} else if (e.key == "ArrowDown") {
if (index < maxIndex) {
index++;
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450);
}
}
}
I'm calling those by body event listeners :
document.addEventListener("scroll", (e) => e.preventDefault())
document.body.addEventListener("keydown", keyScroll);
document.body.addEventListener("wheel", scrollHandler,);
document.body.addEventListener("touchstart", touchStart, false);
document.body.addEventListener("touchmove", touchMove, false);
It's working perfectly => example at https://darleanow.github.io .
But when i'm on smartphone (Iphone), scroll might bug and the height of divs would appear different, you can try it on your own smartphone to see what i'm talking about, try scrolling a bit fast etc...
If anybody has an idea :)
This isn't a solution necessarily, but you might try printing scroll[0].stlye.marginTop to the console every time it changes.
If that value is never erroneous, I'd guess the issue is with the animation (maybe scrolling while it's animating, or something like that).

How to detect collision?

I am building a video game where a spaceship moves with controllers and it must avoid some fireballs in order to win. However now I would like to setup a collision system: when a fireball touches the spaceship, game is over (alert("game over")). Any help with this? Thanks!!!
let spaceship = document.querySelector("#icon")
//Fireball script
const fireballArray = []
function generateFireBallWithAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key])
};
return el
}
function createFireBalls(amount) {
for (let i = 0; i <= amount; i++) {
fireballArray.push(generateFireBallWithAttributes(document.createElement("img"), {
src: "Photo/fireball.png",
width: "40"
}))
}
}
createFireBalls(10)
fireballArray.forEach((fireballElement) => {
const fallStartInterval = setInterval(function() {})
document.querySelector("body").appendChild(fireballElement);
const fireballMovement = {
x: fireballRandom(fireballElement.offsetWidth),
y: 0
}
const fireLoop = function() {
fireballMovement.y += 2;
fireballElement.style.top = fireballMovement.y + "px";
if (fireballMovement.y > window.innerHeight) {
fireballMovement.x = fireballRandom(fireballElement.offsetWidth);
fireballElement.style.left = fireballMovement.x + "px";
fireballMovement.y = 0
}
}
fireballElement.style.left = fireballMovement.x + "px";
let fireInterval = setInterval(fireLoop, 1000 / ((Math.random() * (50)) + 75))
})
function fireballRandom(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
//Spaceship moves into space + prevent going out borders
let hits = 0
let pos = {
top: 1000,
left: 570
}
const keys = {}
window.addEventListener("keydown", function(e) {
keys[e.keyCode] = true
})
window.addEventListener("keyup", function(e) {
keys[e.keyCode] = false
})
const loop = function() {
if (keys[37] || keys[81]) {pos.left -= 5}
if (keys[39] || keys[68]) {pos.left += 5}
if (keys[38] || keys[90]) {pos.top -= 4}
if (keys[40] || keys[83]) {pos.top += 4}
if (pos.left < 0) { pos.left = -1}
if (pos.top < 0) {pos.top = -1}
if (pos.left + spaceship.offsetWidth >= body.offsetWidth) {
pos.left = body.offsetWidth - spaceship.offsetWidth
}
if (pos.top + spaceship.offsetHeight >= body.offsetHeight) {
pos.top = body.offsetHeight - spaceship.offsetHeight
}
spaceship.setAttribute("data", body.offsetWidth + ":" + body.offsetHeight)
spaceship.style.left = pos.left + "px";
spaceship.style.top = pos.top + "px"
}
let sensibilty = setInterval(loop, 8)
<body>
<img src="Photo/Spaceship1.png" id="icon">
</body>
Check this out (hitboxes)
Also this
Your code is missing complete information. That said, as your game progresses with explicit and independent setIntervals on fireballs and spaceship, and you dont want to make bigger changes I suggest you call the checkCollision() function any time fireball moves or spaceship moves (that is in the loop and fireloop "animation functions").
The collision itself is done with the following:
function detectOverlap(fireball) {
const shipRect = spaceship.getBoundingClientRect();
const fireballRect = fireball.getBoundingClientRect();
//these two variables are of following type
//DOMRect {x: 570, y: 836, width: 104.484375, height: 112.828125, ...}
// this will test if the ship collides with specific fireball
if (
(shipRect.x + shipRect.width > fireballRect.x && shipRect.x < fireballRect.x + fireball.width)
&& (shipRect.y + shipRect.height > fireballRect.y && shipRect.y < fireballRect.y + fireball.height)
) {
return true;
} else {
return false;
}
};
function checkCollision() {
let collision = false;
fireballArray.forEach(fireball => {
if (detectOverlap(fireball)) { collision = true };
});
// this is satisfied if the ship collides with any fireball
if (collision === true) {
// do some logic on collision
console.log('collision');
}
};
This will do rectangle vs rectangle collision detection. This might not fit the best, but it is a good first approximation.
NOTE: You can check the devtools console (F12) to assess the correct collision detection.

Setting timer of seconds and milli seconds

var count = 24000,
running = true,
secondsNode = document.getElementById("seconds"),
millisecondsNode = document.getElementById("milliseconds"),
mOld,
mNew;
function draw() {
if (count > 0 && running) {
requestAnimationFrame(draw);
mNew = new Date().getTime();
count = count - mNew + mOld;
count = count >= 0 ? count : 0;
mOld = mNew;
secondsNode.innerHTML = Math.floor(count / 1000);
millisecondsNode.innerHTML = count % 1000;
}
}
mOld = new Date().getTime();
draw();
window.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 32: // PLAY
if (running) {
running = false;
} else {
running = true;
mOld = new Date().getTime();
draw();
}
break;
case 82: // RESET
count = 24000;
secondsNode.innerHTML = 24;
millisecondsNode.innerHTML = 0;
running = false;
}
});
<p><span id="seconds">4</span> secs and <span id="milliseconds">000</span> milliseconds</p>
This is the code for timer. Here what is happening is the timer starting from 24 seconds and ending in 0. But what I need is I need to start this timer from 0 to 4 seconds. Can we do that? If that so please help. Thanks:)
In order to achieve this, you need to change the time decrementer to an incrementer by using:
count = count + mNew - mOld;
Also, you need to ensure that your conditions and checks stop when you reach 4000 instead of 0.
See working example below:
var count = 0,
running = false,
secondsNode = document.getElementById("seconds"),
millisecondsNode = document.getElementById("milliseconds"),
mOld,
mNew;
function isElementInViewport(el) { // run function to check if the element is in the viewport
var rect = el.getBoundingClientRect();
return rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.top < (window.innerHeight || document.documentElement.clientHeight);
}
window.addEventListener('scroll', function() { // everytime we scroll
if(isElementInViewport(secondsNode)) { // check if the p element is on the screen - if it is then:
running = true; // start the timer (unpause it)
mOld = new Date().getTime();
draw();
} else { // if the element is off the screen then
running = false; // pause the timer
}
});
function draw() {
if (count < 4000 && running) { // change to check count < 4000 to keep running
requestAnimationFrame(draw);
mNew = new Date().getTime();
count = count + mNew - mOld; // change to increment the count
count = count >= 4000 ? 4000 : count; // change stop the clock from incrementing
mOld = mNew;
secondsNode.innerHTML = Math.floor(count / 1000);
millisecondsNode.innerHTML = count % 1000;
}
}
window.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 32: // PLAY (space)
if (running) {
running = false;
} else {
running = true;
mOld = new Date().getTime();
draw();
}
break;
case 82: // RESET (r)
count = 0;
secondsNode.innerHTML = 0;
millisecondsNode.innerHTML = 0;
running = true;
}
});
.other {
padding-bottom: 100vh;
}
<div class="other"></div>
<p><span id="seconds">4</span> secs and <span id="milliseconds">000</span> milliseconds</p>
<div class="other"></div>

Touch events seem to work fine in Chrome Devtools, but don't respond on mobile devices

While using the mobile mode in Chrome dev tools, everything works fine. However, as soon as I upload my code to the server and try it on my iPhone, it is unresponsive.
Here is the a link to the unresponsive app and below is the code for my event listeners..
Link: Running App
Code:
// touch timer to stop and start
time.addEventListener('touchstart', (e) => {
if (running === false){
running = true;
timer = setInterval(startTimer, 100);
}
else {
running = false;
clearInterval(timer);
}
});
// touch '+' to increase speed by 0.5 mph
up.addEventListener('touchstart', (e) => {
mph += 0.5;
speed.innerHTML = `${mph.toFixed(1)} mph`
});
// touch '-' to decrease speed by 0.5 mph
down.addEventListener('touchstart', (e) => {
if (mph >= 0.5){
mph -= 0.5;
speed.innerHTML = `${mph.toFixed(1)} mph`
}
});
//Gathers start position for finger swipe
speedRow.addEventListener('touchstart', (e) => {
xStart = e.changedTouches[0].pageX;
});
// Swipe finger to change speed
speedRow.addEventListener('touchmove', (e) => {
e.preventDefault();
xEnd = e.changedTouches[0].pageX;
if (xStart < xEnd){
mph += (Math.abs(xEnd - xStart)/1500)
}
else if (xStart > xEnd && mph > 0) {
mph -= (Math.abs(xEnd - xStart)/1500)
}
speed.innerHTML = `${mph.toFixed(1)} mph`
});
Not sure if this answers your question, but mac desktop safari is throwing an error when your JS loads because "speed" is already a property on window that it doesn't want overwritten. It's likely that iOS safari is throwing the same error. EDIT: And that error may be stopping code execution.
You should either namespace all those constants in an object. Or wrap all of your code in an iffy to prevent that. You can't be sure what properties will already exist in the global context.
Update:
Also, on the live site you're not passing 'e' in the 'touchstart' listener for speedRow, that could also be stopping execution on iOS safari.
UPDATE WITH THE ACTUAL ANSWER
Just tested the live site on my iPhone. iOS safari is erroring and stopping execution because you're trying to overwrite the global time variable. Looks like it's working in chrome because Chrome doesn't have a global time variable. If you wrap your code in a iffy like this it will work:
(function () {var mili = 0;
var sec = 0;
var min = 0;
var running = false;
var timer;
var mph = 0;
var distanceTraveled = 0;
var xStart;
var xEnd;
const navbar = document.getElementById('navbar');
const time = document.getElementById('time');
const elapsedTime = document.getElementById('time');
const up = document.getElementById('increase');
const down = document.getElementById('decrease');
const runSpeed = document.getElementById('speed');
const distance = document.getElementById('distance');
const speedRow = document.getElementById('speedRow');
function startTimer(){
mili += 100;
if (mili > 999){
mili = 0;
sec +=1;
distanceTraveled += (1/3600) * mph;
if (sec > 59){
sec = 0;
min += 1;
}
}
if (sec < 10){
var strSec = `0${sec}`;
}
else{
strSec =`${sec}`;
}
if (min < 10){
var strMin = `0${min}`;
}
else{
strMin =`${min}`;
}
if(mili < 100){
if(mili === 0){
strMili = '00'
}
else{
var strMili = `${mili}`;
strMili = strMili.slice(0, -1);
strMili = `0${strMili}`;
}
}
else{
strMili = `${mili}`;
strMili = strMili.slice(0, -1);
}
elapsedTime.innerHTML = `${strMin}:${strSec}:${strMili}`;
distance.innerHTML = `${distanceTraveled.toFixed(2)} miles`;
}
time.addEventListener('touchstart', () => {
if (running === false){
running = true;
timer = setInterval(startTimer, 100);
}
else {
running = false;
clearInterval(timer);
}
});
up.addEventListener('touchstart', () => {
mph += 0.5;
runSpeed.innerHTML = `${mph.toFixed(1)} mph`
});
down.addEventListener('touchstart', () => {
if (mph >= 0.5){
mph -= 0.5;
runSpeed.innerHTML = `${mph.toFixed(1)} mph`
}
});
speedRow.addEventListener('touchstart', (e) => {
xStart = e.changedTouches[0].pageX;
});
speedRow.addEventListener('touchmove', (e) => {
e.preventDefault();
xEnd = e.changedTouches[0].pageX;
if (xStart < xEnd){
mph += (Math.abs(xEnd - xStart)/1500)
}
else if (xStart > xEnd && mph > 0) {
mph -= (Math.abs(xEnd - xStart)/1500)
}
runSpeed.innerHTML = `${mph.toFixed(1)} mph`
});
})()
If you have a mac you can use desktop Safari's inspector to debug mobile safari.

How to create a pause in recursion function loop

I'm new here so sorry if dublicate.
I'm trying to write a sudoku solver using backtracking method to make it pass all data dynamically into the document with its each recursion. When I tried to use setInterval() or setTimeout() it doesn't work the way I want it to work. What I need should be similar to this
Here's the code:
function backtrack(position) {
if (position === 81) {
return true;
}
if (sudokuArray[position] > 0) {
backtrack(position + 1);
} else {
for (var x = 1; x <= 9; x++) {
if (isValid(x, parseInt(position / 9), position % 9) === true) {
sudokuArray[position] = x;
//some code that invokes putSudokuArrayBack function with a small delay
//everytime backtrack function is invoked
if (backtrack(position + 1) === true) {
return true;
}
}
}
sudokuArray[position] = 0;
return false;
}
}
function putSudokuArrayBack() {
for (var i = 0; i <= 81; i++) {
var indexString = '#val-' + parseInt(i / 9) + '-' + i % 9;
$(indexString).val(sudokuArray[i]);
}
}
Any ideas(if it's even possible)? Thanks in advance!

Categories

Resources