This is for a demo... and i was just curious, can you detect if the window has been moved? Like if you move Firefox/Chrome/IE around your monitor? I doubt it, but I wanted to see since you can check for resize and focus/blurred windows.
I can only think of this (heavy) work-around, where you check if window.screenX and window.screenY have changed every x milliseconds
var oldX = window.screenX,
oldY = window.screenY;
var interval = setInterval(function(){
if(oldX != window.screenX || oldY != window.screenY){
console.log('moved!');
} else {
console.log('not moved!');
}
oldX = window.screenX;
oldY = window.screenY;
}, 500);
Though I would not recommend this -- it might be slow and I'm not sure if screenX and screenY are supported by all browsers
A potentially more optimised version of this is to only check for window movement when outside of the window combined with Harmen's answer:
var interval;
window.addEventListener("mouseout", function(evt){
if (evt.toElement === null && evt.relatedTarget === null) {
//if outside the window...
if (console) console.log("out");
interval = setInterval(function () {
//do something with evt.screenX/evt.screenY
}, 250);
} else {
//if inside the window...
if (console) console.log("in");
clearInterval(interval);
}
});
If using jQuery, it may normalise screenX/Y in this case so it's worth running a few tests on that. Jquery would use this format instead of addEventListener:
$(window).on('mouseout', function () {});
If you are moving the window in Windows OS via alt + Space, and find that windows resizes are ignored, I would recommend adding an extra level of detection via keypress events.
Re the first answer: I use the 'poll window position' in production code. It's a very lightweight thing to do. Asking for a couple of object properties twice a second is not going to slow anything down. Cross-browser window position is given by:
function get_window_x_pos()
{
var winx;
if(window.screenX)
winx=window.screenX;
else if(window.screenLeft)
winx=window.screenLeft;
return winx;
}
and similarly for vertical position. In my code I use this to fire an AJAX event off to the server to store position and size of the window so next time it will open where it was the last time (I'm probably moving to HTML5 local storage soon.) One little wrinkle you might want to cover is not generating spurious updates while the window is being dragged. The way to handle this is to register when the window has been moved for the first time and only trigger an update when two subsequent polls of window position return the same value. A further complication is for windows which allow resizing from all sides. If the left or top side are dragged, the DOM will give you a resize event, but the nominal window position will have altered as well.
Unfortunately not. The DOM is only notified about window sizes, cursor positions, "focus" and "blur", etc; anything that affects drawing. Since moving a window doesn't necessarily require any of the contents to be "redrawn" (in a Javascript/Html engine sort of sense), the DOM, therefore, doesn't need to know about it.
Sadly, no. Although I did find this page that claims there is such a thing. I tested that in IE, Chrome, and FireFox, no luck.
Related
I know its a bit to ask, but is the following possible without using jQuery? I have it running with jQuery now but it seems to be presenting performance issues. If you could help I will be most grateful. I am not lazy, just not very code knowledgable. Took me a while to even get this far.
//
// default speed ist the lowest valid scroll speed.
//
var default_speed = 1;
//
// speed increments defines the increase/decrease of the acceleration
// between current scroll speed and data-scroll-speed
//
var speed_increment = 0.01;
//
// maximum scroll speed of the elements
//
var data_scroll_speed_a = 2; // #sloganenglish
var data_scroll_speed_b = 5; // #image-ul
//
//
//
var increase_speed, decrease_speed, target_speed, current_speed, speed_increments;
$(document).ready(function() {
$(window).on('load resize scroll', function() {
var WindowScrollTop = $(this).scrollTop(),
Div_one_top = $('#image-ul').offset().top,
Div_one_height = $('#image-ul').outerHeight(true),
Window_height = $(this).outerHeight(true);
if (WindowScrollTop + Window_height >= (Div_one_top + Div_one_height)) {
$('#sloganenglish').attr('data-scroll-speed', data_scroll_speed_a).attr('data-current-scroll-speed', default_speed).attr('data-speed-increments', data_scroll_speed_a * speed_increment);
$('#image-ul').attr('data-scroll-speed', data_scroll_speed_b).attr('data-current-scroll-speed', default_speed).attr('data-speed-increments', data_scroll_speed_b * speed_increment);
increase_speed = true;
decrease_speed = false;
} else {
$('#sloganenglish').attr('data-scroll-speed', '1').attr('data-current-scroll-speed', default_speed);
$('#image-ul').attr('data-scroll-speed', '1').attr('data-current-scroll-speed', default_speed);
decrease_speed = true;
increase_speed = false;
}
}).scroll();
});
I don't see any performance issue in your code, although there is space for some optimization. And I don't think jQuery might be the problem.
First thing to notice is the CSS access.
The height attribute is very expensive to access because it causes the browser to process many rendering steps of the pipeline, as you can see in CSS Triggers.
You are retrieving the height of two elements in a scroll event, which means that they will be calculated many times. Is it really necessary?
If your #image-ul element doesn't change its height, maybe you can calculate it outside of the event only once.
In the case of the window height, I believe it won't change in the scroll event. How about to create different handlers, one for the events that need to (re)calculate the window height and another for the events that don't need that calculation?
Another noticeable point is that you set the 'data-current-scroll-speed' and the 'data-speed-increments' attribute always with the same constant value. No change, no unset. Is it really necessary?
Actually, it is not clear what you are really doing. Your performance issue might be somewhere else.
some time ago I started to write some code in JavaScript to learn it a little bit. I picked a rollin/rollout animation as 'project'. (I know about JQuery's slideDown/slideUp, but I wanted to work with pure JavaScript.)
I finished my effect, and the result looks pretty good in all major browsers except Firefox (tested versions 22.x to the latest (25.0.1)). In Firefox, the rolling (in and out) stutters while it rolls smoothly in Chrome, Opera, and Internet Explorer.
The general approach is (unsurprisingly) to have an element's style.height (or width) attribute increased/decreased several times by some pixels over a given time. To avoid calculating sizes every time the effect takes place, I calculate them one time and place them in an array (first item (0 + stepSize), last item wanted height/width). The decrease of the element's height is done by this function:
var verticalRollInWorker = function(step) {
if (step > 0) {
$(btt).style.height = stepSizes[step - 1];
setTimeout(function() { verticalRollInWorker(step - 1); }, delay);
} else {
$(btt).style.display = "none";
$(btt).style.height = 0;
// Enable roll out effect:
stateChange(false);
if (afterFullRollIn != null) {
afterFullRollIn();
}
}
}
In the particular example, I'm using 20 steps over 400ms. The step sizes in the array are rounded to integers, that's why the last step just sets 0 - to handle rounding differences.
(For convenience, I wrote my own $(element) helper, there's no JQuery involved here.)
I tested Firefox without Add-Ons, no difference.
I highly appreciate any help you can provide :)
One problem that I noticed in the above code is that you used $(btt). So, every 20s when the function is iterated, the browser needs to obtain the jQuery object. You could rather store it into a variable say 'var BTT=$(btt);' and use this BTT. Fetching jQuery object is a time consuming task.
Since you are using setTimeout(), the function will be executed every 20ms regardless of the completion of the current execution, this may also cause a drag. As Dagg Nabbit said, you could use setInterval() instad of setTimeout.
Another possible reason might be browser-reflow. I made a personalised scrollbar, and found browser reflow was noticeably greater in my FF than Chrome or IE. This depends on the size of the element, DOM tree depth, overflow property, and more...
And again use this code and see if it is fixed. reduces the subtraction into 1 code.
var BTT=$(btt).get(0);
var verticalRollInWorker = function(step) {
if (step > 0) {
step--;
BTT.style.height = stepSizes[step];
setTimeout(function() { verticalRollInWorker(step); }, delay);
}
else {
BTT.style.display = "none";
BTT.style.height = 0;
// Enable roll out effect:
stateChange(false);
if (afterFullRollIn != null) {
afterFullRollIn();
}
}
}
Further Comments can be made only after seeing a live example.
Regards.
I've got a demo that is required to run in Firefox on a Windows 7 touch tablet. According to this, Mozilla has implemented a standardized touch API. However, this does not work on a windows 7 tablet. None of these events are triggered in FF 14.
we have to use the MozTouchMove event. But all it does is dispatch sequential events. WI.e. finger one then finger two then finger three etc.
It's difficult to even distinguish two fingers from one. I'd have to measure the distance between updates the assign my own "IDs" to each "Region". After that, to detect a two-finger drag, we'd have to make sure the parsing stays the same throughout--which if one finger goes up may "undrag" as the "second" position is overwritten by the "first" position. Trying to come up with an approach. Any ideas?
// multitouch event handler for two finger scrolling in x or y
function onTouchMove(event) {
var eventTouch = new point(event.clientX, event.clientY);
if (previousTouch.x == 0 && previousTouch.y == 0) {
previousTouch = new point(eventTouch.x, eventTouch.y);
return;
}
//filter really close touches
//in this case, assume single touch and defer to system mouse
if (previousTouch !== undefined) {
if (eventTouch.distance(previousTouch) < 6) {
return;
}
}
fingerIndex ++;
// only track every other touch to keep fingers consistent
if( fingerIndex % 2 == 0)
{
document.getElementById("finger1").style.left = previousTouch.x + "px";
document.getElementById("finger1").style.top = previousTouch.y + "px";
document.getElementById("finger2").style.left = eventTouch.x + "px";
document.getElementById("finger2").style.top = eventTouch.y + "px";
}
previousTouch = eventTouch;
}
The status document you link to is dated July 2010 - Mozilla was close, two years ago. So you are now using the deprecated single-touch API and attempting to implement multi-touch - this is bound to get ugly. The documentation actually points you to the new multi-touch API that is available since Firefox 12. This lets you distinguish the touches properly and the documentation actually explains in much detail how you would do it.
The problem I am having is with this code:
function scrollLeft() {
document.body.scrollLeft -= scrollSpeed;
}
It works perfectly in Chrome and Safari but in IE and Firefox it is doing nothing. I have narrowed this down to the fact that in Firefox and IE standards mode they accept document.documentElement.scrollLeft instead of document.body.scrollLeftand my page is being rendered in standards mode.
Please note that I run this script at an interval of 10ms so I can't afford to have a large clunky detection script running each time this function needs to perform.
Does anyone know a cross browser way or an extremely light way of overcoming this? I am using the jQuery library in this script but Firefox and IE aren't recognising $('body').scrollLeft() either and I'm not sure why that is.
KISS: use scrollBy
window.scrollBy( -scrollSpeed, 0 )
Use the logical or operator to fall back.
document[body || documentElement].scrollLeft -= scrollSpeed
This is very quick, so very little overhead for you.
For bonus points do this instead
scrollLeft = (function () {
var docEl = document[body || documentElement];
return function () {
docEl.scrollLeft -= scrollSpeed;
};
})();
This only finds the relevant element reference once and closes over the temporary variable that holds it.
I'm trying to detect the position of the browser's scrollbar with JavaScript to decide where in the page the current view is.
My guess is that I have to detect where the thumb on the track is, and then the height of the thumb as a percentage of the total height of the track. Am I over-complicating it, or does JavaScript offer an easier solution than that? What would some code look like?
You can use element.scrollTop and element.scrollLeft to get the vertical and horizontal offset, respectively, that has been scrolled. element can be document.body if you care about the whole page. You can compare it to element.offsetHeight and element.offsetWidth (again, element may be the body) if you need percentages.
I did this for a <div> on Chrome.
element.scrollTop - is the pixels hidden in top due to the scroll. With no scroll its value is 0.
element.scrollHeight - is the pixels of the whole div.
element.clientHeight - is the pixels that you see in your browser.
var a = element.scrollTop;
will be the position.
var b = element.scrollHeight - element.clientHeight;
will be the maximum value for scrollTop.
var c = a / b;
will be the percent of scroll [from 0 to 1].
document.getScroll = function() {
if (window.pageYOffset != undefined) {
return [pageXOffset, pageYOffset];
} else {
var sx, sy, d = document,
r = d.documentElement,
b = d.body;
sx = r.scrollLeft || b.scrollLeft || 0;
sy = r.scrollTop || b.scrollTop || 0;
return [sx, sy];
}
}
returns an array with two integers- [scrollLeft, scrollTop]
It's like this :)
window.addEventListener("scroll", (event) => {
let scroll = this.scrollY;
console.log(scroll)
});
Answer for 2018:
The best way to do things like that is to use the Intersection Observer API.
The Intersection Observer API provides a way to asynchronously observe
changes in the intersection of a target element with an ancestor
element or with a top-level document's viewport.
Historically, detecting visibility of an element, or the relative
visibility of two elements in relation to each other, has been a
difficult task for which solutions have been unreliable and prone to
causing the browser and the sites the user is accessing to become
sluggish. Unfortunately, as the web has matured, the need for this
kind of information has grown. Intersection information is needed for
many reasons, such as:
Lazy-loading of images or other content as a page is scrolled.
Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't
have to flip through pages.
Reporting of visibility of advertisements in order to calculate ad revenues.
Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.
Implementing intersection detection in the past involved event
handlers and loops calling methods like
Element.getBoundingClientRect() to build up the needed information for
every element affected. Since all this code runs on the main thread,
even one of these can cause performance problems. When a site is
loaded with these tests, things can get downright ugly.
See the following code example:
var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#listItem');
observer.observe(target);
Most modern browsers support the IntersectionObserver, but you should use the polyfill for backward-compatibility.
If you care for the whole page, you can use this:
document.body.getBoundingClientRect().top
Snippets
The read-only scrollY property of the Window interface returns the
number of pixels that the document is currently scrolled vertically.
window.addEventListener('scroll', function(){console.log(this.scrollY)})
html{height:5000px}
Shorter version using anonymous arrow function (ES6) and avoiding the use of this
window.addEventListener('scroll', () => console.log(scrollY))
html{height:5000px}
Here is the other way to get the scroll position:
const getScrollPosition = (el = window) => ({
x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
});
If you are using jQuery there is a perfect function for you: .scrollTop()
doc here -> http://api.jquery.com/scrollTop/
note: you can use this function to retrieve OR set the position.
see also: http://api.jquery.com/?s=scroll
I think the following function can help to have scroll coordinate values:
const getScrollCoordinate = (el = window) => ({
x: el.pageXOffset || el.scrollLeft,
y: el.pageYOffset || el.scrollTop,
});
I got this idea from this answer with a little change.