Addition operators not working - javascript

I'm trying to make one-page scrolling but I have a small problem. I have
var main = document.getElementById('main');
window.addEventListener('scroll', function(){
window.scrollTo(0, 0);
var top = -main.offsetTop;
main.style.top = -100 + '%';
});
Now this only works once obviously, so I'm trying to put an addition operator so it deducts a 100% every time you scroll.
main.style.top -= 100 + '%';
but that doesn't work for some reason..
Addition works if I do this instead...
var top = main.offsetTop;
main.style.top = top - 100 + '%';
but this fucks things up since top is technically in px not %. So any ideas how to make main.style.top -= 100 + '%'; work or a good alternative instead?

Let me write the expression main.style.top -= 100 + '%'; in another way that means the same thing:
main.style.top -= (100 + '%' /* this is a string now */)
What happens if you -= a string from anything? The first expression that you put (main.style.top = top - 100 + '%';) was closer to what you want but still not right. If you want to subtract 100% of the top value every time, like you said you need to do it in terms of pixels, not percent. So first you need to find how many pixels is 100 percent, which looking at your code might just be the value of main.offsetTop for you. Then you need to subtract that value from the top value. So what you have first is close but I think you need:
var main = document.getElementById('main');
window.addEventListener('scroll', function(){
window.scrollTo(0, 0);
var top = main.offsetTop;
main.style.top -= top;
});

Related

Problem with the '.style.transform=' in JS

I have this SVG header that, on screen scroll, its parts move differently. For this, I am using a script like the following:
let menu = document.getElementById('main-menu');
let lua = document.getElementById('lua');
window.addEventListener('scroll', function(){
let value = window.scrollY;
menu.style.marginTop = value * 0.45 + 'px'; // working just fine
lua.style.transform = "translateY(" + value * 0.25 + ")"; // don't know how to make this work
OBS: I need to use the transform: translate because this SVG element, for some reason, can't be moved with margin or top/left, only translate.
The menu.style work just fine, but on the lua.style case I'm struggling cause I don't know how to write on the JS a CSS such as transform: translateY that merges a child property (translate) inside another property (transform).
I've tried to write the lua.style.transform = "translateY(" + value * 0.25 + ")"; in many different ways, like:
lua.style.transform = value + "translateY(" * 0.25 + ")";
// or
lua.style.translate = value * 0.45 + 'px';
// or
lua.style.translate = value * 0.45;
// or
lua.style.translate = (0,value * 0.45);
but still can't make it work. How can I write it correctly?
DONE! I used the template literal that Dane Landry explained in the comments. Thanks man.
The code:
lua.style.transform = ` translateY(${value * 0.25}px ) `;
And also thanks kmoser for reminding me translate demans a unit of measurement. It does.

Login to increase integer var from min value to max value using jquery scroll function

I'm struggling to see the how the logic is working inside the parseInt.
Would anyone be able to help with some logic (if possible) to replace the current -x/10 below.
Rather than starting at 0, and then adding to it. I am trying to achieve spanning between 30% and 70%.
$('ARTICLE A.thumb>SPAN').scroll(function(){
var x = $(this).scrollTop();
$(this).css('background-position','center '+parseInt(-x/10)+'%');
});
So the idea is when I'm at the top of the page, the integer will be 30% and as i slowly scroll down the page, this will increase, but when I reach the bottom of the page, it will be 70%. So the min is 30 and the max is 70.
My logic understanding is not great as I'm more UI focused but would really appreciate some help on this one.
I've made this into a fidde, but I'm trying to output as HTML string so you people can see what is happening. But I can't even get this work now :/
https://jsfiddle.net/w6h2z52n/3/
Firstly your JSFiddle wasn't pulling in jQuery - ooops!
Also the scroll should occur on the window element so I have updated the JSFiddle to reflect this -
https://jsfiddle.net/w6h2z52n/7/
$(window).on('scroll', function(){
// get our distance from top
var x = $(this).scrollTop();
// determine our percentage. The higher the
// speed the slower the percentage will increment
var speed = 30;
var perc = x > 0 ? parseInt(x / speed) : 0;
// now convert our percentage to between 30 and 70
if(perc <= 30) perc = 30;
if(perc >= 70) perc = 70;
$('.value SPAN').html('background-position: center ' + perc + '%');
});
I have added a check in to make sure the percentage is between 30 & 70 although this may not be exactly what you are looking for and some kind of logical operation to translate the percentage to a value between 30 & 70 may be better but let me know!
EDIT
This was a nice little challenge so I gave it another go, it's just a case of mathematics... give it a go!
https://jsfiddle.net/w6h2z52n/10/
$(window).on('scroll', function(){
// get our distance from top
var x = $(this).scrollTop();
// work out our containers height
var $container = $('.container');
// determine percentage from top of page
var percFromTop = ( x / $container.outerHeight() ) * 100;
percFromTop = Math.floor(percFromTop);
// now convert our percentage to between 30 and 70
perc = Math.floor( 30 + (30 / 70) * percFromTop );
$('.value SPAN').html('background-position: center ' + perc + '%');
});
uh this really made me interested, i solved it the following way
// remember last x
var lastx = 0
// the overall height
var overallheight = $(document).height()
// 70 - 30 == 50, so divide overallheight through 50 to get the "1%" in px
var unit = (overallheight / 50)
// initial percent
var perc = 30
$(window).scroll(function(){
var x = $(this).scrollTop();
// if scroll down more than lastx + "1%"
if(x >= lastx + unit){
// determine how many percent(step) the actual "gap" is (rounded)
gap = x - lastx;
step = parseInt( gap / unit)
// add percent
perc += step
lastx = x
}
// same reversed for scrolling back up
if(x <= lastx + unit){
gap = lastx - x;
step = parseInt( gap / unit)
perc -= step
lastx = x
}
$('.value').html('background-position: center '+parseInt(-x/10)+'%');
});
for me this attempt feels quite smooth, but there is some small error due to the round(), maybe extracheck perc does not get smaller than 30 greater than 70
https://jsfiddle.net/w6h2z52n/11/

Adjust position of a group of adjacent divs in non-linear fashion

This isn't so much a jQuery question as it is an overall conceptual question.
In my example I can populate a container with divs that have a top value set in a nonlinear fashion.
The top value of each one is calculated based on a formula that takes into account the top position of the one to its left as well as the height of the container (line 33 of fiddle).
//this formula sets the top value for each new child added to the container
//height is 100% of its parent which is 20% of the body
//newOne:last is the most recently added child and will have an initial top value of 10%
parseInt($(this).next().css('top'), 10) / $('#queue').height()) * 75 + (parseInt($('.newOne:last').css('top'), 10) * 2) + '%'
I more of less stumbled upon this by chance and it seems to work 'ok', but if an optimization is obvious to you, please point it out :)
What I'm having trouble coming up with is an elegant formula for how to adjust the children smoothly during a drag event. I'm thinking the top value needs to be adjusted based on some manipulation of the left offset, but after hours of experimenting, I haven't found anything that keeps the original position intact when I start dragging and continues adjusting the values smoothly during my drag. The children should gradually approach a minimum top value of 10% as I drag left (child with left offset of 0 will have a top value of 10%), and gradually move away from that top value back toward their initial position as I drag right.
$('#queue').draggable({
axis: "x",
scroll: false,
drag: function(){
//adjust values of each child
$('.newOne').each(function(){
var percentLeft = $(this).offset().left / $('footer').width() * 100
var thisLeft = parseInt($(this).css('left'), 10) / $(window).width() * 100;
var thisTop = parseInt($(this).css('top'), 10) / $('#queue').height() * 100;
if (percentLeft >= 0){
//top value of each one gradually decreases...
//as it gets closer to an offset value of 0 and minimum top value of 10%
//non-linear attempt but not even close
//$(this).css('top', $(this).css('top', 10 + (thisTop - 10 / thisLeft) + '%'));
//linear step
$(this).css({'top': 8 + (percentLeft/2) + '%'});
}
});
}
});
http://jsfiddle.net/5RRCS/17/
P.S. I know I'm asking a lot here, but hopefully someone is up to the challenge :)
Update:
Stumbled onto exp method and did something like this:
adjustTop = function(offset){
return 100 * (1.0-Math.min(0.98,(0.83 + ( 0.17/ (Math.exp(0.007*offset))) )) ) + '%';
};
$(this).css('top', adjustTop($(this).offset().left) );
Here's a version that I believe does what you are looking for.
The first thing I did was to refactor the top calculation so that both the initialization and the drag handlers would get the same results.
Rather than calculate the positions of the child divs based on their offset to the document, I changed the logic to use position relative to their container.
I also remove z-index as the child divs already being added the parent with the correct stacking order - the left most child is the last element in the container.
Calculating the height of each child depended on whether #queue's current position was to the left or right of its origin.
I also change the iteration logic to behave the same to simplify calculating the current elements starting offset:
$($('.newOne').get().reverse()).each(function (index) {
$(this).css({
'background': 'rgba(255,255,255,.80)',
'top': calcTop($(this), index)
});
});
Code for positioning the child elements:
function calcTop($ele, index) {
var elePositionLeft = $ele.position().left;
var queuePositionLeft = $('#queue').position().left;
var footerWidth = $('footer').width();
var queueHeight = $('#queue').height();
var distanceToTravel = queuePositionLeft < 0 ? elePositionLeft : footerWidth - elePositionLeft;
var percentTraveled = Math.abs(queuePositionLeft) / distanceToTravel;
var thisPercentLeft = (elePositionLeft + queuePositionLeft) / footerWidth;
var queuePercentLeft = queuePositionLeft / footerWidth;
var newTop;
var myStartOffset = (index + 1) * startOffset;
var topTravel = queuePositionLeft < 0 ? -myStartOffset + startOffset : (queueHeight - startOffset);
var linear = false;
if (linear) {
newTop = myStartOffset + (topTravel * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
return newTop;
} else {
if (queuePositionLeft >= 0) {
newTop = myStartOffset + (topTravel * thisPercentLeft * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
} else {
newTop = myStartOffset + (topTravel * (1+thisPercentLeft) * percentTraveled);
newTop = newTop < startOffset ? startOffset : Math.round(newTop);
}
return newTop;
}
}
There was also a minor bug in the reset function - it wasn't setting childCount back to zero:
$('#reset').click(function () {
$('#queue').empty().css('left', 0);
childCount = 0;
});
Demo Fiddle

iScroll Scrolling Past Bottom?

You can easily see the problem on the first page here: http://m.vancouverislandlife.com/
Scroll down (slide up) and allow the content to leave the page, and it doesn't bounce back and is lost forever. However, on pages whose content does overflow the page and is therefore supposed to be scrollable, the scrolling works correctly (see Accomodations > b&b's and scroll down for an example of this).
I noticed that on my computer, the scrolling on the first page is always stuck at -899px. I can't find anybody else who's experienced this problem and no matter what I try, I just can't fix it! Help!
(It's not exactly urgent, however, as the target audience of iPhones and iPod Touches aren't affected by this since they have so little screen room.)
Okay, new problem. To solve the iScroll issue, I just created a custom script. However, it's not working correctly on the actual device. On desktop browsers, it works just fine. On mobile, it occasionally jumps back to the top and won't recognize some touches. This is probably because of the way I cancelled the default event and had to resort to a bit of a hack. How can I fix this? (Yup - simple problem for a +500 bounty. Not bad, huh?)
Here's the script, and the website is at the usual place:
function Scroller(content) {
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
function getFirstElementChild(element) {
element = element.firstChild;
while(element && element.nodeType !== 1) {
element = element.nextSibling;
}
return element;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
var momentum = 0;
if("createTouch" in document) {
content.addEventListener('touchstart', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
evt.preventDefault();
}, false);
content.addEventListener('touchmove', function(evt) {
if(isScrolling) {
evt = evt.touches[0];
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('touchend', function(evt) {
isScrolling = false;
}, false);
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
function scrollToTop() {
cScroll = 0;
content.style.WebkitTransform = '';
}
function performAnimations() {
if(!isScrolling) {
var firstElementChild = getFirstElementChild(content);
cScroll = range(cScroll + momentum, -(firstElementChild.scrollHeight - content.offsetHeight), 0);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
momentum *= Scroller.FRICTION;
}
}
return {
scrollToTop: scrollToTop,
animationId: setInterval(performAnimations, 33)
}
}
Scroller.MAX_MOMENTUM = 100;
Scroller.ACCELERATION = 1;
Scroller.FRICTION = 0.8;
I think Andrew was on the right track with regards to setting the height of the #wrapper div. As he pointed out that,
that.maxScrollY = that.wrapperH - that.scrollerH;
Normally, this would work. But now that you've changed your #content to position: fixed, the wrapper element is no longer "wrapping" your content, thus that.wrapperH has a value of 0, things break.
Disclaimer: I did not go through the entire script so I may be wrong here
When manually setting a height to #wrapper, say 500px, it becomes,
that.maxScrollY = 500 - that.scrollerH;
The folly here is that when there's a lot of content and the window is small, that.scrollerH is relatively close in value to 500, say 700px. The difference of the two would be 200px, so you can only scroll 200 pixels, thus giving the appearance that it is frozen. This boils down to how you set that maxScrollY value.
Solution (for Chrome browser at least):
Since #wrapper effectively contains no content, we cannot use it in the calculations. Now we are left with the only thing that we can reliably get these dimensions from, #content. In this particular case, it appears that using the content element's scrollHeight yield what we want. This is most likely the one that has the expected behavior,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight;
scrollerH is the offsetHeight, which is roughly the height of what you see in the window. scroller.scrollHeight is the height that's considered scrollable. When the content does not exceed the length of the page, they are roughly equivalent to one another. That means no scroll. When there are a lot of content, the difference of these two values is the amount of scroll you need.
There is still a minor bug, and this looks like it's already there. When you have a lot of content, the last few elements are covered up by the bar when scrolled to the bottom. To fix this, you can set an offset such as,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight - 75;
The number 75 arbitrary. It's probably best if it's the height of the bar itself with 2 or 3 pixels for a bit of padding. Good luck!
Edit:
I forgot to mention last night, but here are the two sample pages that I used in trying to debug this problem.
Long page
Short page
This may be a CSS issue. In your stylesheet (mobile.css line 22), try removing position:fixed from #content.
That should allow the document to scroll normally (vertical scrollbar on a computer, "slideable" on a mobile browser).
Elements with position:fixed exit the normal flow of the document, their positioning is relative to the browser window. This is probably why you're having issues with scrolling. Fixed positioning is generally for elements which should always remain in the same place, even when the page is scrolled (ie. a notification bar "pinned" at the top of a page).
No definite solution, but more a direction I'd go for:
#wrapper and #content's overflow:hidden paired #content's postion:fixed and seem to be the cause of the issue.
If position: fixed is removed from #content, scrolling is possible but the "blank" divs are wrongly layered (tested in Firefox 5).
Your wrapper div seems to have a height of 0. So all the calculations are negative, setting it's height to the window height will correct the scroll issue. When I manually set the wrappers height via firebug and chromes debug bar the scroll functions as it should.
You #content div seems to have its size change on resize, probably a better idea to have the #wrapper div have its size change and then have #content inherit the size.
[Edit]
You don't believe me so codez, From iscroll-lite.js
refresh: function () {
var that = this,
offset;
that.wrapperW = that.wrapper.clientWidth;
that.wrapperH = that.wrapper.clientHeight;
that.scrollerW = that.scroller.offsetWidth;
that.scrollerH = that.scroller.offsetHeight;
that.maxScrollX = that.wrapperW - that.scrollerW;
that.maxScrollY = that.wrapperH - that.scrollerH;
In your page that translates to,
that.wrapperH = 0;
that.maxScrollY = -that.scrollerH
When a scroll finishes, this code gets called.
var that = this,
resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
resetY = that.y >= 0 || that.maxScrollY > 0 ? 0 : that.y < that.maxScrollY ? that.maxScrollY : that.y;
...
that.scrollTo(resetX, resetY, time || 0);
See that that.maxScrollY > 0 ? ? If maxScrollY is negative then scrolling up will never bounce back.
I ended up just making my own, small script to handle the scrolling:
// A custom scroller
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
if("createTouch" in document) {
// TODO: Add for mobile browsers
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
var firstElementChild = content.getElementsByTagName("*")[0];
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
and modifying a few other parts. It does save a lot of download time, I suppose, also.
I'm still going to accept answers and award the bounty in 5 days, though.
Changed question warrants a new answer. I took a look at the code and I saw that you calculated the momentum on each step of the "move" function. This does not make sense because the momentum is used after the move has ended. What this meant was to capture the mouse position at the beginning, and then calculate the difference at the end. So I added two new variables,
var startTime;
var startY;
Inside the start event (mousedown/touchstart), I added,
startY = evt.pageY;
startTime = evt.timeStamp || Date.now();
Then I have the following for my end handler,
var duration = (evt.timeStamp || Date.now()) - startTime;
if (duration < 300) {
var dY = evt.pageY - startY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
} else {
momentum = 0;
}
I also removed the momentum calculation from inside of mousemove/touchmove. Doing this removed the jumping around behavior that I was seeing on my iPhone. I am seeing other unwanted behaviors as well (the whole window "scrolls"), but I'm guessing that you've been working to get rid of those so I didn't attempt.
Good luck. Here's a coded up page that I duplicated for my testing. I also took the liberty to refactor the code for this section to remove some duplicated code. It's under mobile3.js if you want to look at it.

How to move around an image with javascript?

I'm developing a simple web quiz and using javascript, I would like to create an effect that displays a small image (1UP) that wanders around the "game deck" when users reach a specific level or score; user could gain an extra life simply clicking on it in time.
Do you know any Jquery plugin or javascript snippet to achieve an effect like this?
It's actually surprisingly easy to do this:
Create the element:
img = document.createElement('img');
Set its source:
img.src = "myimage.png";
Position it absolutely and such:
img.style.position = "absolute";
img.style.left = "50px";
img.style.top = "50px";
img.style.width = "50px"; // Make these match the image...
img.style.height = "50px"; // ...or leave them off
(Obviously, use whatever coordinates and size you want.)
You may want to make sure it appears above other things:
img.style.zIndex = 100; // Or whatever
Add it to the document:
document.body.appendChild(img);
Move it around
Use window.setInterval (or setTimeout depending on how you want to do it) to move it around by changing its style.left and style.top settings. You can use Math.random to get a random floating point number between 0 and 1, and multiply that and run it through Math.floor to get a whole number for changing your coordinates.
Example
This creates an image at 50,50 and animates it (in a very jittery random way; I didn't spend any time making it look nifty) every fifth of a second for 10 seconds, then removes it:
function createWanderingDiv() {
var img, left, top, counter, interval;
img = document.createElement('img');
img.src = "myimage.png";
left = 200;
top = 200;
img.style.position = "absolute";
img.style.left = left + "px";
img.style.top = top + "px";
img.style.width = "200px"; // Make these match the image...
img.style.height = "200px"; // ...or leave them out.
img.style.zIndex = 100; // Or whatever
document.body.appendChild(img);
counter = 50;
interval = 200; // ms
window.setTimeout(wanderAround, interval);
function wanderAround() {
--counter;
if (counter < 0)
{
// Done; remove it
document.body.removeChild(img);
}
else
{
// Animate a bit more
left += Math.floor(Math.random() * 20) - 10;
if (left < 0)
{
left = 0;
}
top += Math.floor(Math.random() * 10) - 5;
if (top < 0)
{
top = 0;
}
img.style.left = left + "px";
img.style.top = top + "px";
// Re-trigger ourselves
window.setTimeout(wanderAround, interval);
}
}
}
(I prefer re-scheduling on each iteration via setTimeout [as above] to using setInterval, but it's totally your call. If using setInterval, remember the interval handle [return value from setInterval and use window.clearTimeout to cancel it when you're done.)
The above is raw DOM/JavaScript; jQuery offers some helpers to make it a bit simpler, but as you can see, it's pretty straightforward even without.
There's also a jQuery function that can be used to move thing's around.
See this for examples:
http://api.jquery.com/animate/

Categories

Resources