Window vertically re-sizing issue - javascript

Im trying to resize the window. it works horizentally but not vertically. What am I doing wrong here:
$(window).resize(resizeWebSite());
function resizeWebSite(){
$("#panel").height($(window).height() - 10);
$("#map").height($(window).height() - 10);
console.log($("#map").height() / 2);
var loaderTop = ($("#map").height() - $("#loadingIndicator").height()) / 100 + $("#map").position().top;
var loaderLeft = (($("#map").width() - $("#loadingIndicator").width()) / 100) + $("#map").position().left;
$("#loadingIndicator").css({'position' : 'absolute' , 'left' : loaderLeft + 'px', 'top' : loaderTop + 'px'});
}
resizeWebSite();

When you set up the event handler, you need to pass only the name of the function:
$(window).resize(resizeWebSite);
Now a further problem with this will be that on a desktop/laptop machine (traditional computer, as opposed to a tablet), where the user can resize the browser window interactively, the browser will fire "resize" events very rapidly. All that work inside the handler will make the response very sluggish and jumpy.
To counteract that, you can use a timer. The idea is to respond to a "resize" event by first cancelling any pending timer, and then setting a new timer for some time in the future (a hundred milliseconds or so) to run the actual code that deals with the new window size. That way, while the user is moving the mouse quickly to change the window size, the only work you're doing is clearing and resetting a timer, which is pretty fast. Only when the user stops moving the mouse for a while do you actually do any real work.
That would look something like this:
var resizeTimer = null;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(resizeWebSite, 100);
});

Maybe this is what you're after?
function resizeWebSite(){
$("#panel").height($(window).height() - 10);
$("#map").height($(window).height() - 10);
console.log($("#map").height() / 2);
var loaderTop = ($("#map").height() - $("#loadingIndicator").height()) / 100 + $("#map").position().top;
var loaderLeft = (($("#map").width() - $("#loadingIndicator").width()) / 100) + $("#map").position().left;
$("#loadingIndicator").css({'position' : 'absolute' , 'left' : loaderLeft + 'px', 'top' : loaderTop + 'px'});
}
$(document).ready(resizeWebSite);
$(window).resize(resizeWebSite);
By the way, if you're using margin on the elements you're reading the height on, and you want to include them in the calculations, use outerWidth(true) instead of width()

Related

Converting an example jQuery function to vanilla JS: apparent trouble with Element.offsetTop

I'm not even sure exactly what I'm asking, so bear with me.
I'm trying to replicate the effect in this cool pen from Krz Szzz
Here's the original jQuery:
$('.tile')
// tile mouse actions
.on('mouseover', function(){
$(this).children('.photo').css({'transform': 'scale('+ $(this).attr('data-
scale') +')'});
})
.on('mouseout', function(){
$(this).children('.photo').css({'transform': 'scale(1)'});
})
.on('mousemove', function(e){
$(this).children('.photo').css({'transform-origin': ((e.pageX -
$(this).offset().left) / $(this).width()) * 100 + '% ' + ((e.pageY -
$(this).offset().top) / $(this).height()) * 100 +'%'});
})
And here's my attempt to nativize the jQuery (I've tried to reduce the complexity to focus on the issue at hand, so my markup is a bit different than his/hers, but not in relevant ways, I don't believe).
var tile = document.querySelector('.tile');
var tileWidth = tile.offsetWidth;
var tileHeight = tile.offsetHeight;
var tileTop = tile.offsetTop;
var tileLeft = tile.offsetLeft;
tile.addEventListener('mouseover', function(){
tile.style.transform = "scale(1.5)";
});
tile.addEventListener('mousemove', function(e){
tile.style.transformOrigin = ((e.pageX - tileLeft) / tileWidth) * 100 + "%" + ((e.pageY - tileTop) / tileHeight) * 100 + "%";
});
tile.addEventListener('mouseout', function(){
tile.style.transform = "scale(1)";
});
My version halfway works. But there's a calculation error somewhere in there that prevents setting the correct value for the second parameter of the transform-origin CSS.
I believe the issue has to do with $(this).offset().top in the jQuery. I've converted this to Element.offsetTop, but that may not be correct. I've burned 1.5 hours trying to learn my way to success here on SO, but I'm not making any headway.
Any pointers in the right direction would be greatly appreciated.
jQuery offset().top() gets coords related to the document while js offsetTop is related to the parentNode.
You may give Element.getBoundingClientRect().top a try.
It'll give you the top position related to the viewport, not to the document. But it's a beginning.
Then, you can get the scroll position with document.documentElement.scrollTop and sum both of them.
PS: document.documentElement.scrollTop doesn't work on Safari, as I'm concerned. Use document.body.scrollTop instead.

jQuery scroll event: how to determine amount scrolled (scroll delta) in pixels?

I have this event:
$(window).scroll(function(e){
console.log(e);
})
I want to know, how much I have scroll value in pixels, because I think, scroll value depends from window size and screen resolution.
Function parameter e does not contains this information.
I can store $(window).scrollTop() after every scroll and calculate difference, but can I do it differently?
The "scroll value" does not depend on the window size or screen resolution. The "scroll value" is simply the number of pixels scrolled.
However, whether you are able to scroll at all, and the amount you can scroll is based on available real estate for the container and the dimensions of the content within the container (in this case the container is document.documentElement, or document.body for older browsers).
You are correct that the scroll event does not contain this information. It does not provide a delta property to indicate the number of pixels scrolled. This is true for the native scroll event and the jQuery scroll event. This seems like it would be a useful feature to have, similar to how mousewheel events provide properties for X and Y delta.
I do not know, and will not speculate upon, why the powers-that-be did not provide a delta property for scroll, but that is out of scope for this question (feel free to post a separate question about this).
The method you are using of storing scrollTop in a variable and comparing it to the current scrollTop is the best (and only) method I have found. However, you can simplify this a bit by extending jQuery to provide a new custom event, per this article: http://learn.jquery.com/events/event-extensions/
Here is an example extension I created that works with window / document scrolling. It is a custom event called scrolldelta that automatically tracks the X and Y delta (as scrollLeftDelta and scrollTopDelta, respectively). I have not tried it with other elements; leaving this as exercise for the reader. This works in currrent versions of Chrome and Firefox. It uses the trick for getting the sum of document.documentElement.scrollTop and document.body.scrollTop to handle the bug where Chrome updates body.scrollTop instead of documentElement.scrollTop (IE and FF update documentElement.scrollTop; see https://code.google.com/p/chromium/issues/detail?id=2891).
JSFiddle demo: http://jsfiddle.net/tew9zxc1/
Runnable Snippet (scroll down and click Run code snippet):
// custom 'scrolldelta' event extends 'scroll' event
jQuery.event.special.scrolldelta = {
delegateType: "scroll",
bindType: "scroll",
handle: function (event) {
var handleObj = event.handleObj;
var targetData = jQuery.data(event.target);
var ret = null;
var elem = event.target;
var isDoc = elem === document;
var oldTop = targetData.top || 0;
var oldLeft = targetData.left || 0;
targetData.top = isDoc ? elem.documentElement.scrollTop + elem.body.scrollTop : elem.scrollTop;
targetData.left = isDoc ? elem.documentElement.scrollLeft + elem.body.scrollLeft : elem.scrollLeft;
event.scrollTopDelta = targetData.top - oldTop;
event.scrollTop = targetData.top;
event.scrollLeftDelta = targetData.left - oldLeft;
event.scrollLeft = targetData.left;
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = handleObj.type;
return ret;
}
};
// bind to custom 'scrolldelta' event
$(window).on('scrolldelta', function (e) {
var top = e.scrollTop;
var topDelta = e.scrollTopDelta;
var left = e.scrollLeft;
var leftDelta = e.scrollLeftDelta;
// do stuff with the above info; for now just display it to user
var feedbackText = 'scrollTop: ' + top.toString() + 'px (' + (topDelta >= 0 ? '+' : '') + topDelta.toString() + 'px), scrollLeft: ' + left.toString() + 'px (' + (leftDelta >= 0 ? '+' : '') + leftDelta.toString() + 'px)';
document.getElementById('feedback').innerHTML = feedbackText;
});
#content {
/* make window tall enough for vertical scroll */
height: 2000px;
/* make window wide enough for horizontal scroll */
width: 2000px;
/* visualization of scrollable content */
background-color: blue;
}
#feedback {
border:2px solid red;
padding: 4px;
color: black;
position: fixed;
top: 0;
height: 20px;
background-color: #fff;
font-family:'Segoe UI', 'Arial';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='feedback'>scrollTop: 0px, scrollLeft: 0px</div>
<div id='content'></div>
Note that you may want debounce the event depending on what you are doing. You didn't provide very much context in your question, but if you give a better example of what you are actually using this info for we can provide a better answer. (Please show more of your code, and how you are using the "scroll value").
To detemine how many pixels were scrolled you have to keep in mind that the scroll event gets fired almost every pixel that you move. The way to accomplish it is to save the previous scrolled value and compare that in a timeout. Like this:
var scrollValue = 0;
var scrollTimeout = false
$(window).scroll(function(event){
/* Clear it so the function only triggers when scroll events have stopped firing*/
clearTimeout(scrollTimeout);
/* Set it so it fires after a second, but gets cleared after a new triggered event*/
scrollTimeout = setTimeout(function(){
var scrolled = $(document).scrollTop() - scrollValue;
scrollValue = $(document).scrollTop();
alert("The value scrolled was " + scrolled);
}, 1000);
});
This way you will get the amount of scrolled a second after scrolling (this is adjustable but you have to keep in mind that the smooth scrolling that is so prevalent today has some run-out time and you dont want to trigger before a full stop).
The other way to do this? Yes, possible, with jQuery Mobile
I do not appreciate this solution, because it is necessary to include heavy jQuery mobile. Solution:
var diff, top = 0;
$(document).on("scrollstart",function () {
// event fired when scrolling is started
top = $(window).scrollTop();
});
$(document).on("scrollstop",function () {
// event fired when scrolling is stopped
diff = Math.abs($(window).scrollTop() - top);
});
To reduce the used processing power by adding a timer to a Jquery scroll method is probably not a great idea. The visual effect is indeed quite bad.
The whole web browsing experience could be made much better by hiding the scrolling element just when the scroll begins and making it slide in (at the right position) some time after. The scrolling even can be checked with a delay too.
This solution works great.
$(document).ready(function() {
var element = $('.movable_div'),
originalY = element.offset().top;
element.css('position', 'relative');
$(window).on('scroll', function(event) {
var scrollTop = $(window).scrollTop();
element.hide();
element.stop(false, false).animate({
top: scrollTop < originalY
? 0
: scrollTop - originalY + 35
}, 2000,function(){element.slideDown(500,"swing");});
});
});
Live demo here

Javascript memory leak - Canvas HTML5 jQuery

Im building a website that uses canvas primarily, the only cannvas involved however is a line thats drawn horizontally, the line is about 13000px long.
When the user scrolls my window then scrolls horizontally along m canvas path, Example.
Ive notived that on firefox (version 6.0.2) my document fails to scroll. In my console I receive something along the lines of (NS_ERROR_OUT_OF_MEMORY).
After googling this I've found that it could be a potential memory leak? How does this work, is it because of the way I've written my code? or is this a browser/hardware issue?
Im re-initisalising my function on window resize etc and I'm curious as to whether this may have any imapct?
// Initate the plugin
$(window).resize(function() {
if(this.resizeTO) clearTimeout(this.resizeTO);
this.resizeTO = setTimeout(function() {
$(this).trigger('resizeEnd');
}, 500);
});
$(window).bind('resizeEnd', function() {
$("#path").scrollPath({drawPath: true, wrapAround: false});
});
$("#path").scrollPath({drawPath: true, wrapAround: false});
$(document).ready(init);
$('.wrapper').css({'top' : '0px','left' : '0px'});
$('.wrapper > div').css({'height' : + $(window).height() +'px'});
function init() {
// Set window height and width variables
var windowheight = $(window).height();
var windowwidth = $(window).width();
// Check monitor size and workot if incentives needs extra space etc
var bff = 4020 + (1993 - windowwidth);
// Move divs into position
$('.culture').css('top', + - windowheight + 'px');
$('.careerpath').css('top', + - windowheight + 'px');
$('.training').css('top', + - windowheight + 'px');
$('.apply').css('top' , + - windowheight + 'px');
/* ========== DRAWING THE PATH AND INITIATING THE PLUGIN ============= */
$.fn.scrollPath("getPath")
// Move to 'start' element
.moveTo(0, 0, {name: "div"})
.lineTo(2400, 0, {name: "div1"})
.lineTo((bff-550), 0, {name: "div2"})
.lineTo(bff, 0, {name: "div3"})
.lineTo(bff, -windowheight, {name: "div4"})
.lineTo((bff + 1993), -windowheight, {name: "div5"})
.lineTo((bff + 1993 + 1837), -windowheight, {name: "div6"})
.lineTo((bff + ((1993 + 1837 + 1795) - 325)), -windowheight, {name: "div7"})
// We're done with the path, let's initate the plugin on our wrapper element
// Window resize function
$(window).resize(function() {
if(this.resizeTO) clearTimeout(this.resizeTO);
this.resizeTO = setTimeout(function() {
$(this).trigger('resizeEnd');
}, 500);
});
$(window).bind('resizeEnd', function() {
$("#path").scrollPath({drawPath: true, wrapAround: false});
});
$("#path").scrollPath({drawPath: true, wrapAround: false});
}
Ok, now that I googled for the plugin you used I know what is going on.
http://joelb.me/scrollpath/
The "line" is in fact a shape and the scrollPath is generating a nice big canvas for that. The problem is inside the scrollPath stuff. It creates too many canvas instances or leaks something.
You should trace/document the bug a bit better and report it to the author.
The suggestion to create the path from a single DOM element is invalid now that we know you didn't mean a single straight line. I have no idea what is your trget exactly, but you might be able to achieve it with impress.js
You're doing it wrong. This approach will lead to nothing but pain.
I don't think you have a leak, you simply have a memory hog of a program. And other than memory you will also have huge performance issues with this. 2D canvas is heavily affected by fillrate (number of pixels drawn). Drawing this many pixels will be incredibly slow, even on fast computers.
So do NOT make a gigantic canvas and then scroll the window/viewport over it. Instead, make a small canvas, which renders only the visible portion of a bigger thing.

How to add fade effect while changing CSS with JQuery

I am faking fixed position for a footer on a mobile site for mobile browsers that don't support fixed-position. (iOS before iOS 5, Andriod before 2.2, etc.)
Here is the JQuery code I'm using, which works well and does what I want:
function changeFooterPosition() {
$('.not-fixed').css('top', window.innerHeight + window.scrollY - 56 + "px");
}
$(document).bind('scroll', function() {
changeFooterPosition();
});
So that works.
My question is, I want to add a slight delay to it and have the footer fade into view rather than just snap quickly after every little scroll. I've looked around and found the following methods I could use, but I"m not sure if they are the correct ones or where to add them to the js above.
.delay(1000).fadeTo('slow', 1)
I know this functionality exists in JQuery Mobile, but I don't want to use the entirety of JQuery Mobile for just this one little thing.
Thanks in advance.
Try the animate function http://api.jquery.com/animate/
This won't fade but should move smoothly instead.
function changeFooterPosition() {
$('.not-fixed').animate({'top': window.innerHeight + window.scrollY - 56 + "px"}, 2000);
}
$(document).bind('scroll', changeFooterPosition);
Change
$(document).bind('scroll', function() {
changeFooterPosition();
});
To
$(document).bind('scroll', changeFooterPosition);
Change
$('.not-fixed').css('top', window.innerHeight + window.scrollY - 56 + "px");
to
var WantedSpeed = 2000;
$('.not-fixed').delay(1000).animate({
top: window.innerHeight + window.scrollY - 56 + "px"
}, WantedSpeed, function() {
// Animation complete.
})
What you want to do is throttle your scroll callback:
(function() {
var scrollTimer = 0,
$notFixed = $('.not-fixed');
function changeFooterPosition() {
$notFixed.css('top', window.innerHeight + window.scrollY - 56 + "px").show(300);
}
$(document).bind('scroll', function() {
$notFixed.hide();
clearTimeout(scrollTimer);
setTimeout(changeFooterPosition, 50);
});
}());

How do I get an element to scroll into view, using jQuery?

I have an HTML document with images in a grid format using <ul><li><img.... The browser window has both vertical & horizontal scrolling.
Question:
When I click on an image <img>, how then do I get the whole document to scroll to a position where the image I just clicked on is top:20px; left:20px ?
I've had a browse on here for similar posts...although I'm quite new to JavaScript, and want to understand how this is achieved for myself.
There's a DOM method called scrollIntoView, which is supported by all major browsers, that will align an element with the top/left of the viewport (or as close as possible).
$("#myImage")[0].scrollIntoView();
On supported browsers, you can provide options:
$("#myImage")[0].scrollIntoView({
behavior: "smooth", // or "auto" or "instant"
block: "start" // or "end"
});
Alternatively, if all the elements have unique IDs, you can just change the hash property of the location object for back/forward button support:
$(document).delegate("img", function (e) {
if (e.target.id)
window.location.hash = e.target.id;
});
After that, just adjust the scrollTop/scrollLeft properties by -20:
document.body.scrollLeft -= 20;
document.body.scrollTop -= 20;
Since you want to know how it works, I'll explain it step-by-step.
First you want to bind a function as the image's click handler:
$('#someImage').click(function () {
// Code to do scrolling happens here
});
That will apply the click handler to an image with id="someImage". If you want to do this to all images, replace '#someImage' with 'img'.
Now for the actual scrolling code:
Get the image offsets (relative to the document):
var offset = $(this).offset(); // Contains .top and .left
Subtract 20 from top and left:
offset.left -= 20;
offset.top -= 20;
Now animate the scroll-top and scroll-left CSS properties of <body> and <html>:
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
});
Simplest solution I have seen
var offset = $("#target-element").offset();
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
}, 1000);
Tutorial Here
There are methods to scroll element directly into the view, but if you want to scroll to a point relative from an element, you have to do it manually:
Inside the click handler, get the position of the element relative to the document, subtract 20 and use window.scrollTo:
var pos = $(this).offset();
var top = pos.top - 20;
var left = pos.left - 20;
window.scrollTo((left < 0 ? 0 : left), (top < 0 ? 0 : top));
Have a look at the jQuery.scrollTo plugin. Here's a demo.
This plugin has a lot of options that go beyond what native scrollIntoView offers you. For instance, you can set the scrolling to be smooth, and then set a callback for when the scrolling finishes.
You can also have a look at all the JQuery plugins tagged with "scroll".
Here's a quick jQuery plugin to map the built in browser functionality nicely:
$.fn.ensureVisible = function () { $(this).each(function () { $(this)[0].scrollIntoView(); }); };
...
$('.my-elements').ensureVisible();
After trial and error I came up with this function, works with iframe too.
function bringElIntoView(el) {
var elOffset = el.offset();
var $window = $(window);
var windowScrollBottom = $window.scrollTop() + $window.height();
var scrollToPos = -1;
if (elOffset.top < $window.scrollTop()) // element is hidden in the top
scrollToPos = elOffset.top;
else if (elOffset.top + el.height() > windowScrollBottom) // element is hidden in the bottom
scrollToPos = $window.scrollTop() + (elOffset.top + el.height() - windowScrollBottom);
if (scrollToPos !== -1)
$('html, body').animate({ scrollTop: scrollToPos });
}
My UI has a vertical scrolling list of thumbs within a thumbbar
The goal was to make the current thumb right in the center of the thumbbar.
I started from the approved answer, but found that there were a few tweaks to truly center the current thumb. hope this helps someone else.
markup:
<ul id='thumbbar'>
<li id='thumbbar-123'></li>
<li id='thumbbar-124'></li>
<li id='thumbbar-125'></li>
</ul>
jquery:
// scroll the current thumb bar thumb into view
heightbar = $('#thumbbar').height();
heightthumb = $('#thumbbar-' + pageid).height();
offsetbar = $('#thumbbar').scrollTop();
$('#thumbbar').animate({
scrollTop: offsetthumb.top - heightbar / 2 - offsetbar - 20
});
Just a tip. Works on firefox only
Element.scrollIntoView();
Simple 2 steps for scrolling down to end or bottom.
Step1: get the full height of scrollable(conversation) div.
Step2: apply scrollTop on that scrollable(conversation) div using the value
obtained in step1.
var fullHeight = $('#conversation')[0].scrollHeight;
$('#conversation').scrollTop(fullHeight);
Above steps must be applied for every append on the conversation div.
After trying to find a solution that handled every circumstance (options for animating the scroll, padding around the object once it scrolls into view, works even in obscure circumstances such as in an iframe), I finally ended up writing my own solution to this. Since it seems to work when many other solutions failed, I thought I'd share it:
function scrollIntoViewIfNeeded($target, options) {
var options = options ? options : {},
$win = $($target[0].ownerDocument.defaultView), //get the window object of the $target, don't use "window" because the element could possibly be in a different iframe than the one calling the function
$container = options.$container ? options.$container : $win,
padding = options.padding ? options.padding : 20,
elemTop = $target.offset().top,
elemHeight = $target.outerHeight(),
containerTop = $container.scrollTop(),
//Everything past this point is used only to get the container's visible height, which is needed to do this accurately
containerHeight = $container.outerHeight(),
winTop = $win.scrollTop(),
winBot = winTop + $win.height(),
containerVisibleTop = containerTop < winTop ? winTop : containerTop,
containerVisibleBottom = containerTop + containerHeight > winBot ? winBot : containerTop + containerHeight,
containerVisibleHeight = containerVisibleBottom - containerVisibleTop;
if (elemTop < containerTop) {
//scroll up
if (options.instant) {
$container.scrollTop(elemTop - padding);
} else {
$container.animate({scrollTop: elemTop - padding}, options.animationOptions);
}
} else if (elemTop + elemHeight > containerTop + containerVisibleHeight) {
//scroll down
if (options.instant) {
$container.scrollTop(elemTop + elemHeight - containerVisibleHeight + padding);
} else {
$container.animate({scrollTop: elemTop + elemHeight - containerVisibleHeight + padding}, options.animationOptions);
}
}
}
$target is a jQuery object containing the object you wish to scroll into view if needed.
options (optional) can contain the following options passed in an object:
options.$container - a jQuery object pointing to the containing element of $target (in other words, the element in the dom with the scrollbars). Defaults to the window that contains the $target element and is smart enough to select an iframe window. Remember to include the $ in the property name.
options.padding - the padding in pixels to add above or below the object when it is scrolled into view. This way it is not right against the edge of the window. Defaults to 20.
options.instant - if set to true, jQuery animate will not be used and the scroll will instantly pop to the correct location. Defaults to false.
options.animationOptions - any jQuery options you wish to pass to the jQuery animate function (see http://api.jquery.com/animate/). With this, you can change the duration of the animation or have a callback function executed when the scrolling is complete. This only works if options.instant is set to false. If you need to have an instant animation but with a callback, set options.animationOptions.duration = 0 instead of using options.instant = true.

Categories

Resources