Back to top button without jQuery - javascript

I want to create a button "Back to top" with javascript. My code (which I found on StackOverflow) does not work when I click the button nothing happens.
HTML
<button type="button" id="backtotop_js">To the top</button>
JAVASCRIPT
document.getElementById('backtotop_js').onclick = function () {
scrollTo(document.documentElement, 0, 1250);
};
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if(t < 1)
return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
(I'm using Chrome and Firefox)
Where's the mistake?

Will make the scroll to top without animation in vanilla JS
document.getElementById('backtotop_js').onclick = function () {
document.documentElement.scrollTop = 0;
}
EDIT:
changed document.getElementsByTagName('body')[0] to document.documentElement as per Rudie's comment below.

Definition and Usage (of document.document.Element)
The documentElement property returns the documentElement of a document, as an Element object.
For HTML documents the returned object is the HTML element.
Note: If the HTML element is missing, the return value is null.
http://www.w3schools.com/jsref/prop_document_documentelement.asp
you shoud scroll not the html element but instead the body... as vikton was giving an example.

Related

Javascript Animation on scroll, Vanilla JS

I want to use this code that i have seen on github, but I don't know how to apply this code on my HTML, to have an scrolling effect.
The point is, I don't know how to run use this code
source https://gist.github.com/andjosh/6764939
document.getElementsByTagName('button')[0].onclick = function () {
scrollTo(document.body, 0, 1250);
}
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
First you have to replace document.body with document.documentElement, as document.body.scrollTop() has been deprecated.
Edit: it seems that I was not completely right about document.body.scrollTop() being deprecated. The best solution to support multiple browsers is to check for both cases.
Second, you need to set a value > 0 for the 'to' parameter, as Quantastical already pointed out.
Also make sure you have a <button> element. It should work then.

How to use requestAnimationFrame inside a Class object

I have a class that takes some coordinate and duration data. I want to use it to animate an svg. In more explicit terms, I want to use that data to change svg attributes over a time frame.
I'm using a step function and requestAnimationFrame outside the class:
function step(timestamp) {
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(step);
}
}
var circleMove = new SingleLineAnimation(3000, startXY, endXY)
var start = null
function runProgram() {
window.requestAnimationFrame(step);
}
I can make it a method, replacing the circleLine with this. That works fine for the first run through, but when it calls the this.step callback a second time, well, we're in a callback black hole and the reference to this is broken. Doing the old self = this won't work either, once we jump into the callback this is undefined(I'm not sure why). Here it is as a method:
step(timestamp) {
var self = this;
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(self.totalFrames(), self.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < self.duration) {
window.requestAnimationFrame(self.step);
}
}
Any ideas on how to keep the "wiring" inside the Object?
Here's the code that more or less works with the step function defined outside the class.
class SingleLineAnimation {
constructor(duration, startXY, endXY) {
this.duration = duration;
this.xLine = [ startXY[0], endXY[0] ];
this.yLine = [ startXY[1], endXY[1] ];
}
totalFrames(framerate = 60) { // Default to 60htz ie, 60 frames per second
return Math.floor(this.duration * framerate / 1000);
}
frame(progress) {
return this.totalFrames() - Math.floor((this.duration - progress) / 17 );
}
}
This will also be inserted into the Class, for now it's just a helper function:
function distancePerFrame(totalFrames, startEndPoints) {
return totalFrames > 0 ? Math.floor(Math.abs(startEndPoints[0] - startEndPoints[1]) / totalFrames) : 0;
}
And click a button to...
function runProgram() {
window.requestAnimationFrame(step);
}
You need to bind the requestAnimationFrame callback function to a context. The canonical way of doing this is like this:
window.requestAnimationFrame(this.step.bind(this))
but it's not ideal because you're repeatedly calling .bind and creating a new function reference over and over, once per frame.
If you had a locally scoped variable set to this.step.bind(this) you could pass that and avoid that continual rebinding.
An alternative is this:
function animate() {
var start = performance.now();
el = document.querySelector('#start');
// use var self = this if you need to refer to `this` inside `frame()`
function frame(timestamp) {
var progress = timestamp - start;
var currentX = parseInt(el.getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
el.setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(frame);
}
}
window.requestAnimationFrame(frame);
}
i.e. you're setting up the initial state, and then doing the animation within a purely locally scoped function that's called pseudo-recursively by requestAnimationFrame.
NB: either version of the code will interact badly if you inadvertently call another function that initiates an animation at the same time.

HTML progress element not updating

This is a problem I've been struggling with for a while now. I've implemented an HTML5 progress element like so:
<progress id="progress" value="0" max="100"></progress><output class="percent" id="percent">0</output><span class="prozent">%</span>
Suppose I update the value of progress and percent in a JS loop:
i = 0;
pBar = document.getElementById('progress');
pBar.value = i;//start at 0%
percent = 1;//set target for next progress bar update in %
//DO
do {
//IF(i > i_max) EXIT
//CALL VERLET
VERLET(i, x, v, a, time, const0, gamma, A_o, omega, dt);
x_plot[i] = [time[i], x[i]];
v_plot[i] = [time[i], v[i]];
a_plot[i] = [time[i], a[i]];
//i = i + 1
i = i + 1;
if (parseInt(i / i_max * 100, 10) === percent) {
pBar.value = percent;//update progress bar
document.getElementById('percent').innerHTML = percent;
percent = percent + 1;
}
//END DO
} while (i < i_max);
This is the relevant portion of my script. Please assume all variables are properly declared and initialized, and the script as whole is linted. A draft of the page can be found here.
I don't understand why the values of the progress and output elements are not updated until after the calculation is finished. Or this is what appears to happen. IE11(?) seems to show some updating when initially loading the page, but again doesn't update the progress bar for a new calculation without reloading.
Here is something along similar lines, but not using this modern method. I've taken a stab at getting this example to work, without success. This example, though better written I think, might shed some light on the problem.
Is it a timeout issue? Is there a simple jQuery method I could make use of? Any feedback on this problem will be appreciated.
You can't do those kind of operations in a single while-loop.
What happens is the loop will run until it ends and you'll only be able to see the result when it finishes the operation.
For this kind of problems you should go for an asynchronous way.
function updateDom() {
// do stuff
setTimeout(updateDom, 500);
}
setTimeout(updateDom, 500);
As #epascarello says, you cant update the DOM in a while loop.
Update it with an interval:
var interval = setInterval(function () {
VERLET(i, x, v, a, time, const0, gamma, A_o, omega, dt);
x_plot[i] = [time[i], x[i]];
v_plot[i] = [time[i], v[i]];
a_plot[i] = [time[i], a[i]];
i = i + 1;
if (parseInt(i / i_max * 100, 10) === percent) {
pBar.value = percent;//update progress bar
document.getElementById('percent').innerHTML = percent;
percent = percent + 1;
}
if (i < i_max) {
clearInterval(interval);
}
}, 100);
Here's how I got my progress bar to animate:
HTML
<progress id="progress" value="0" max="100"></progress><output class="percent" id="percent">0</output><span class="prozent">%</span>
JS
start = new Date().getTime();
//do stuff
end = new Date().getTime();
t = (end - start);
document.getElementById('progress').value = 0;
document.getElementById('percent').innerHTML = 0;
if (parseInt(t / 100.0, 10) <= 1) {
t = 1;//minimum update interval in ms
} else {
t = parseInt(t / 100.0, 10);//actual update interval in ms
}
go = setInterval(animate, t);
value = 0;
max = 100;
go = 0;
function animate() {
if (value >= max) {
clearInterval(go);
return;
}
value += 1;
document.getElementById('progress').value = value;
document.getElementById('percent').innerHTML = value;
if (value === max) {
clearInterval(go);
}
}
This script can be invoked when the page loads via <body onload="amp()"> or when prompted by the user with <input type="number" name="theta0" id="theta0" value="0.1" min="0.0" max="1.6" step="0.1" onchange="amp()">. As you can see, the update interval is tied to the CPU clock time in ms since I can't update the DOM while in a while loop. It's clunky, but it works.

Record and "playback" css 3d transforms

I'm trying to record a user's mouse and key inputs, then "play them back" to the user by triggering events at chronologically the same time they occurred. That sentence is a little confusing so I'll explain my code:
var $dancerContainer = $('.dancerContainer');
var count = 3;
var captured;
var countInterval;
$dancerContainer is the element I want to animate, which is a div.
count is the duration of the 'recording' phase.
captured will eventually be an object that holds the events, keyed by the elapsed time in milliseconds they occurred since the start of the recording.
function captureInput() {
var mouseCapture = [];
var keyCapture = [];
var start = new Date().getTime();
$(document).on('mousemove.capture', function(event) {
event.t = new Date().getTime() - start;
mouseCapture.push(event);
});
$(document).on('keyup.capture', function(event) {
event.t = new Date().getTime() - start;
keyCapture.push(event);
});
setTimeout(function() {
$(document).off('.capture');
captured = chronoCaptures(mouseCapture, keyCapture);
}, 3000);
}
The captureInput func tags an elapsed time on the event before pushing them to mouse and keyup arrays, mouseCapture and keyCapture, then unbinds the listeners.
function chronoCaptures(mouse, keyboard) {
var greater = (mouse.length > keyboard.length) ? mouse.length : keyboard.length;
var chrono = {};
var j = 0;
var k = 0;
for (var i = 0; i < greater; i++) {
if (keyboard[k] == undefined) {
chrono[mouse[j].t] = mouse[j];
j++;
} else if (mouse[j] == undefined) {
chrono[keyboard[k].t] = keyboard[k];
k++;
} else {
if (mouse[j].t < keyboard[k].t) {
chrono[mouse[j].t] = mouse[j];
j++;
} else {
chrono[keyboard[k].t] = keyboard[k];
k++;
}
}
}
return chrono;
}
chronoOrderCaptures then takes the two arrays of events and returns an object whose keys are the times each event occurred. Now that I look at this code again, since I'm putting the events into an object anyway, it doesn't matter what order they get put in. I might overwrite an event with another, in which case I want the key event to take precedence (going to refactor this, but this is beside the point).
function replayDance(captured, duration) {
var d = duration * 1000;
var start = new Date().getTime();
var elapsed = 0;
while (elapsed <= d) {
elapsed = new Date().getTime() - start;
var c = captured[elapsed];
if (c) {
$dancerContainer.trigger(c)
}
}
}
Finally, replayDance waits for the duration of the recording and checks how much time has elapsed. If the captured object contains an entry # that amount of elapsed time, I trigger the event on the document.
WHEW. Thank you if you've gotten this far. Now to the problem!! (what?) The problem I'm having is the mouse events all get played back 'at once'. There's no pausing occurring, I don't see them executed in sequence as if I was actually moving the mouse, although it seems like they ought to be getting triggered at roughly the same time they were recorded.
Finally here is the handler for mousemove events:
$(document).on('mousemove', followMouse);
function followMouse(event) {
var width = $(window).width();
var height = $(window).height();
var mouseX = event.pageX - (width * 0.25);
var mouseY = event.pageY - (height * 0.25);
var angleX = (mouseY / height) * 45;
var angleY = (mouseX / width) * 45;
dancer.style.webkitTransform = "rotateX(" + angleX + "deg) rotateY(" + angleY + "deg)";
}

Dynamically change style of all <img> tags

I am trying to simulate shaking by changing the position of img tags within a div.
I have it working for one img at a time.
Is there a way to change the style of all the img tags within a div at once?
This is what I am currently doing:
bird = document.createElement('img');
bird.setAttribute('src',birdie);
bird.setAttribute('class', 'birdie');
bird.setAttribute('id', id);
bird.setAttribute('onLoad', 'shakeAll()');
map.appendChild(bird);
birds++;
if(birdmap[0] == 0){
birdmap = [id];
}else{
birdmap+=[,id];
}
this ShakeAll function is also at onLoad of body:
function shakeAll(){
if (birdmap[0] == 0) return;
i = 1;
while(i <= birds){
shakeIt(birdmap[i]);
i++;
}
setTimeout("shakeAll()",initialSpeed);
}
Note: the imgs are absolute
function shakeIt(id){
shake = document.getElementById(id);
j=1;
while(j<4){
if (j==1){
shake.style.top=parseInt(shake.style.top)+distance+"px";
}
else if (j==2){
shake.style.left=parseInt(shake.style.left)+distance+"px";
}
else if (j==3){
shake.style.top=parseInt(shake.style.top)-distance+"px";
}
else{
shake.style.left=parseInt(shake.style.left)-distance+"px";
}
j++;
}
//setTimeout("shakeIt(id)", 50);
}
I couldn't really tell what you were trying to do in your code. Here's some code that shows the basics of moving some images back and forth:
// quadratic easing in/out - acceleration until halfway, then deceleration
// t = time into the animation
// d = duration of the total animation
// b = base value
// c = max change from base value (range)
var easeInOutQuad = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
};
var linearTween = function (t, b, c, d) {
return c*t/d + b;
};
// cubic easing in/out - acceleration until halfway, then deceleration
var easeInOutCubic = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
};
function shakeAll() {
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
// save away original position
for (var i = 0; i < imgs.length; i++) {
imgs[i].basePos = parseInt(imgs[i].style.left, 10);
}
var numShakes = 0;
var maxShakes = 10;
var range = 100;
var direction = 1;
var duration = 300; // ms
var startTime = (new Date()).getTime();
var deltas = [];
function shakeImgs() {
var now = (new Date()).getTime();
var elapsed = Math.min(now - startTime, duration);
var delta = Math.round(easeInOutQuad(elapsed, 0, range, duration));
delta *= direction;
for (var i = 0; i < imgs.length; i++) {
var basePos = imgs[i].basePos;
if (direction < 0) {
basePos += range;
}
imgs[i].style.left = (basePos + delta) + "px";
}
if (now - startTime >= duration) {
startTime = now;
direction *= -1;
++numShakes;
}
if (numShakes < maxShakes) {
setTimeout(shakeImgs, 10);
}
}
shakeImgs();
}
Working demo and HTML shown here: http://jsfiddle.net/jfriend00/ED5yA/
And, here's a fun one that adds some random shakiness (jitter) to the animation: http://jsfiddle.net/jfriend00/jM8jx/.
The basics of obtaining the list of images to operate on is this part:
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
This gets the container object and then gets all image objects in that container. You can see the corresponding HTML in the jsFiddle. This code implements a positioning scheme that slows the velocity down at the edges and goes the fastest in the middle of the range. The rest of the motion is controlled by the intial value of the variables declared in shakeAll(). These can be edited or can be changed to be passed into the function.
give a common class name to all imgs that you wanna shake. and then, user getElementsByClassName() instead of getElementById() to return an array of the elements which have the specific class name. then use a loop to animate each.
but if you want ALL imgs to be animated, use element.getElementsByTagName() or document.getElementsByTagName() instead.

Categories