Why window onscroll event does not work? - javascript

I want to execute the window onscroll event, but I don't know why it doesn't work on all browsers(firefox, chrome, etc), and there is no errors occurred.
Full code:
var elem = document.getElementById('repeat');
var show = document.getElementById('show');
for (i = 1; i <= 300; i++) {
elem.innerHTML += i + "<br/>";
}
window.onscroll = function () {
show.innerHTML = document.body.scrollTop;
};
#show {
display:block;
position:fixed;
top:0px;
left:300px;
}
<pre id="repeat"></pre>
<div style="position:relative;">
<div id="show">x</div>
</div>
Also jsfiddle:
http://jsfiddle.net/sqo0140j
What is the problem ?

You said something interesting:
x changed to 0 and remains as is.
The only way in your code that can happen is if the onscroll function block makes a change because your HTML sets x.
If your window.onscroll = function() is indeed firing, but you are not getting the right scroll position (i.e. 0), try changing the way the scroll position is returned:
window.onscroll = function () {
show.innerHTML = document.documentElement.scrollTop || document.body.scrollTop;
};
I found out that document.documentElement.scrollTop always returns 0 on Chrome. This is because WebKit uses body for keeping track of scrolling, but Firefox and IE use html.
Please try your updated snippet:
var elem = document.getElementById('repeat');
var show = document.getElementById('show');
for (i = 1; i <= 300; i++) {
elem.innerHTML += i + "<br/>";
}
window.onscroll = function () {
show.innerHTML = document.documentElement.scrollTop || document.body.scrollTop;
};
#show {
display:block;
position:fixed;
top:0px;
left:300px;
}
<pre id="repeat"></pre>
<div style="position:relative;">
<div id="show">x</div>
</div>

For me the statement document.body.scrollTop; works well in Chrome and Opera, but on Firefox returns 0.
Viceversa the statement document.documentElement.scrollTop; works good on Firefox but not in Chrome and Opera...
Maybe document.body.scrollTop; is not well supported by FF
Possible Solutions:
I tried:
Math.max(document.body.scrollTop, document.documentElement.scrollTop);
and
document.body.scrollTop || document.documentElement.scrollTop;
They both works well on all above browsers.

I had also the same problem , but I didn't know the proper reason for that .
In my case
window.onscroll = function () {
console.log(document.documentElement.scrollTop || document.body.scrollTop);
};
this code didn't work even after removing margin:0; and padding:0; .
But by mention the addEventListener on the document.body it is worked
document.body.addEventListener('scroll',()=>{
console.log(document.documentElement.scrollTop || document.body.scrollTop);
})

This question has been answered, but I wanted to include details of my situation that prevent onscroll from working.
I have datacontainer that had its own scroll bar which overrides the built in window scroll bar. I didnt notice this until I started giving a more thorough look at the elements on the page using chrome developer. My outer tag looked like this,
This caused two scroll bars to appear on the left. This was because the property, overflow:auto, was telling the data container to make another scroll bar.
Once I removed the overflow:auto I could now correct hit the onscroll event.

Because a lot of people use W3Schools i wanted to leave this here.
This simple scroll event wasn't firing
window.onscroll = function () { myFunction() };
var navbar = document.getElementById("navbar");
var sticky = navbar.offsetTop;
function myFunction() {
if (window.pageYOffset - 20 >= sticky) {
navbar.classList.add("sticky");
}
}
Until I removed < link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
and everything was working again flawlessly.

For people having issues with this function. Another reason that it looks like it is not working is that you define it multiple times.
window.onscroll = function() {
console.log('scroll 1');
};
// some other js code
window.onscroll = function() {
console.log('scroll 2');
};
Only the last one gets executed since it overwrites your first declaration. The solution is then
window.addEventListener('scroll', function() {
console.log('scroll 1');
})
window.addEventListener('scroll', function() {
console.log('scroll 2');
})

Related

How to prevent iOS keyboard from pushing the view off screen with CSS or JS

I have a responsive web page that opens a modal when you tap a button. When the modal opens, it is set to take up the full width and height of the page using fixed positioning. The modal also has an input field in it.
On iOS devices, when the input field is focused, the keyboard opens. However, when it opens, it actually pushes the full document up out of the way such that half of my page goes above the top of the viewport. I can confirm that the actual html tag itself has been pushed up to compensate for the keyboard and that it has not happened via CSS or JavaScript.
Has anyone seen this before and, if so, is there a way to prevent it, or reposition things after the keyboard has opened? It's a problem because I need users to be able to see content at the top of the modal while, simultaneously, I'd like to auto-focus the input field.
first
<script type="text/javascript">
$(document).ready(function() {
document.ontouchmove = function(e){
e.preventDefault();
}
});
then this
input.onfocus = function () {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
}
For anyone stumbling into this in React, I've managed to fix it adapting #ankurJos solution like this:
const inputElement = useRef(null);
useEffect(() => {
inputElement.current.onfocus = () => {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
};
});
<input ref={inputElement} />
I struggled with this for awhile, I couldn't find something that worked well for me.
I ended up doing some JavaScript hackery to make it work.
What I found was that Safari wouldn't push the viewport if the input element was in the top half of the screen. That was the key to my little hack:
I intercept the focus event on the input object and instead redirect the focus to a invisible (by transform: translateX(-9999px)). Then once the keyboard is on screen (usually 200ms or so) I trigger the focus event on the original element which has since animated on screen.
It's a kind of complicated interaction, but it works really well.
function ensureOffScreenInput() {
let elem = document.querySelector("#__fake_input");
if (!elem) {
elem = document.createElement("input");
elem.style.position = "fixed";
elem.style.top = "0px";
elem.style.opacity = "0.1";
elem.style.width = "10px";
elem.style.height = "10px";
elem.style.transform = "translateX(-1000px)";
elem.type = "text";
elem.id = "__fake_input";
document.body.appendChild(elem);
}
return elem;
}
var node = document.querySelector('#real-input')
var fakeInput = ensureOffScreenInput();
function handleFocus(event) {
fakeInput.focus();
let last = event.target.getBoundingClientRect().top;
setTimeout(() => {
function detectMovement() {
const now = event.target.getBoundingClientRect().top;
const dist = Math.abs(last - now);
// Once any animations have stabilized, do your thing
if (dist > 0.01) {
requestAnimationFrame(detectMovement);
last = now;
} else {
event.target.focus();
event.target.addEventListener("focus", handleFocus, { once: true });
}
}
requestAnimationFrame(detectMovement);
}, 50);
}
node.addEventListener("focus", handleFocus, { once: true });
Personally I use this code in a Svelte action and it works really well in my Svelte PWA clone of Apple Maps.
Video of it working in a PWA clone of Apple Maps
You'll notice in the video that the auto-complete changes after the animation of the input into the top half of the viewport stabilizes. That's the focus switch back happening.
The only downside of this hack is that the focus handler on your original implementation will run twice, but there are ways to account for that with metadata.
you could also do this if you don't want scrollTo the top(0, 0)
window.scrollBy(0, 0)
const handleResize = () => {
document.getElementById('header').style.top = window.visualViewport.offsetTop.toString() + 'px'
}
if (window && window.visualViewport) visualViewport.addEventListener('resize', handleResize)
Source: https://rdavis.io/articles/dealing-with-the-visual-viewport
In some situations this issue can be mitigated by re-focusing the input element.
input.onfocus = function () {
this.blur();
this.focus();
}
Both IOS8 and Safari bowsers behave the same for input.focus() occuring after page load. They both zoom to the element and bring up the keyboard.(Not too sure if this will be help but have you tried using something like this?)
HTML IS
<input autofocus>
JS is
for (var i = 0; i < 5; i++) {
document.write("<br><button onclick='alert(this.innerHTML)'>" + i + "</button>");
}
//document.querySelector('input').focus();
CSS
button {
width: 300px;
height: 40px;
}
ALso you will have to use a user-agent workaround, you can use it for all IOS versions
if (!/iPad|iPhone|iPod/g.test(navigator.userAgent)) {
element.focus();
}

document.body.scrollTop value stuck at 0

I have a bit of text that I want to change when the user scrolls a certain distance. However, when I scroll, the value of document.body.scrollTop remains at 0.
var scroll = document.body.scrollTop;
if (scroll < 50) {
document.write("A");
} else {
document.write("B");
}
When checking the log, the value of scroll never budges from 0, thus the text never switches from A to B when scrolling. Thanks for any help in advance.
EDIT: None of the first three answers seem to work for me. I suppose I should provide some context.
Building my design portfolio site. View the early build here. I'd like to be able to change the word "designer" in the banner to other descriptor words as the user scrolls down the page, but can't seem to be able to listen to the current scroll location.
Why are you placing that script inline within the banner? Why not implement your logic within your existing $(window).scroll(function () { as that event seems to be setting the opacity correctly on scroll.
Just add:
if(scrollTop < 50){
$('#banner h1').text("My name is John. I'm a designer");
} else {
$('#banner h1').text("My name is John. I'm a thinker");
}
Live Demo
if(document.attachEvent){
document.attachEvent('onscroll', scrollEvent);
}else if(document.addEventListener){
document.addEventListener('scroll', scrollEvent, false);
}
function scrollEvent(e){
var scroll = document.body.scrollTop;
var text = null;
if (scroll < 50) {
text = document.createTextNode('A');
} else {
text = document.createTextNode('B');
}
document.body.appendChild(text);
}
Though unrelated to your issue, you should stay away from document.write whenever you can. See Why is document.write considered a "bad practice"? for more detail.
this should do it. "document.documentElement.scrollTop" is an IE variant.
should work cross browsers.
window.onscroll = function() {
var scroll = window.scrollY || document.documentElement.scrollTop;
if (scroll < 50) {
document.write("A");
} else {
document.write("B");
}
}
DEMO FIDDLE
var el = $('.test');
//alert(el.scrollTop());
el.on('scroll', function(){
if(el.scrollTop()>50){
alert(el.scrollTop());
}
});
Try this.

how to build gmail like menu header

how can i build fixed menu like gmail menu. i have tried css, but the div stays in middle, it doesnt come up like the gmail menu does on scroll.
open in large image
i have tried using css property, following is some example code (not real code):
.menu {
position:fixed;
height: 36px;
background-color:#fff;
}
You need to use javascript to check the scrollTop and set the position of your menu to fixed if if the scrollTop is more than the height of your header.
function getScrollTop() {
if(typeof pageYOffset!= 'undefined') {
//most browsers
return pageYOffset;
}
else {
var b = document.body; //IE 'quirks'
var d = document.documentElement; //IE with doctype
d = (d.clientHeight) ? d : b;
return d.scrollTop;
}
}
function onScroll() {
var menu = document.getElementById('divMyMenu');
var headerAndNavHeight = document.getElementById('divHeader').clientHeight
+ document.getElementById('tsMain').clientHeight;
if (getScrollTop() < headerAndNavHeight) {
menu.style.top = headerAndNavHeight + 'px';
menu.style.position = 'absolute';
}
else {
menu.style.top = '0px';
menu.style.position = 'fixed';
}
}
A good and easy to use jQuery Plugin for this is Waypoints
Here you can see a working example:
http://imakewebthings.github.com/jquery-waypoints/sticky-elements/
Position fixed alone is not enough to achieve this effect. Also, position:fixed does not work in IE7 or below, so you'll probably want to have fallback.
You need to use javascript (jQuery makes it easy) to dynamically change the position of the element based upon how far scrolled down the page you are.
Look into .scrollTop()
http://api.jquery.com/scrollTop/
var scrollTop = $(window).scrollTop();
May be this is what you are looking for
http://blog.geotitles.com/2011/10/creating-top-fixed-menu-bar-with-css3-buttons-found-in-gmail/
Here is a very simple trick to implement your requirement explained with example and a link to download.
http://itswadesh.wordpress.com/2012/02/24/google-like-top-bar-with-drop-down-menu-using-html-css-and-jquery/

scroll then snap to top

Just wondering if anyone has an idea as to how I might re-create a nav bar style that I saw a while ago, I just found the site I saw it on, but am not sure how they might have gotten there. Basically want it to scroll with the page then lock to the top...
http://lesscss.org/
Just do a quick "view source" on http://lesscss.org/ and you'll see this:
window.onscroll = function () {
if (!docked && (menu.offsetTop - scrollTop() < 0)) {
menu.style.top = 0;
menu.style.position = 'fixed';
menu.className = 'docked';
docked = true;
} else if (docked && scrollTop() <= init) {
menu.style.position = 'absolute';
menu.style.top = init + 'px';
menu.className = menu.className.replace('docked', '');
docked = false;
}
};
They're binding to the onscroll event for the window, this event is triggered when the window scrolls. The docked flag is set to true when the menu is "locked" to the top of the page, the menu is set to position:fixed at the same time that that flag is set to true. The rest is just some simple "are we about to scroll the menu off the page" and "are we about back where we started" position checking logic.
You have to be careful with onscroll events though, they can fire a lot in rapid succession so your handler needs to be pretty quick and should precompute as much as possible.
In jQuery, it would look pretty much the same:
$(window).scroll(function() {
// Pretty much the same as what's on lesscss.org
});
You see this sort of thing quite often with the "floating almost fixed position vertical toolbar" things such as those on cracked.com.
mu is too short answer is working, I'm just posting this to give you the jquery script!
var docked = false;
var menu = $('#menu');
var init = menu.offset().top;
$(window).scroll(function()
{
if (!docked && (menu.offset().top - $("body").scrollTop() < 0))
{
menu.css({
position : "fixed",
top: 0,
});
docked = true;
}
else if(docked && $("body").scrollTop() <= init)
{
menu.css({
position : "absolute",
top: init + 'px',
});
docked = false;
}
});
Mu's answer got me far. I tried my luck with replicationg lesscss.org's approach but ran into issues on browser resizing and zooming. Took me a while to find out how to react to that properly and how to reset the initial position (init) without jQuery or any other library.
Find a preview on JSFiddle: http://jsfiddle.net/ctietze/zeasg/
So here's the plain JavaScript code in detail, just in case JSFiddle refuses to work.
Reusable scroll-then-snap menu class
Here's a reusable version. I put the scrolling checks into a class because the helper methods involved cluttered my main namespace:
var windowScrollTop = function () {
return window.pageYOffset;
};
var Menu = (function (scrollOffset) {
var Menu = function () {
this.element = document.getElementById('nav');
this.docked = false;
this.initialOffsetTop = 0;
this.resetInitialOffsetTop();
}
Menu.prototype = {
offsetTop: function () {
return this.element.offsetTop;
},
resetInitialOffsetTop: function () {
this.initialOffsetTop = this.offsetTop();
},
dock: function () {
this.element.className = 'docked';
this.docked = true;
},
undock: function () {
this.element.className = this.element.className.replace('docked', '');
this.docked = false;
},
toggleDock: function () {
if (this.docked === false && (this.offsetTop() - scrollOffset() < 0)) {
this.dock();
} else if (this.docked === true && (scrollOffset() <= this.initialOffsetTop)) {
this.undock();
}
}
};
return Menu;
})(windowScrollTop);
var menu = new Menu();
window.onscroll = function () {
menu.toggleDock();
};
Handle zoom/page resize events
var updateMenuTop = function () {
// Shortly dock to reset the initial Y-offset
menu.undock();
menu.resetInitialOffsetTop();
// If appropriate, undock again based on the new value
menu.toggleDock();
};
var zoomListeners = [updateMenuTop];
(function(){
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0];
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = w.innerWidth || e.clientWidth || g.clientWidth;
if (lastWidth == widthNow) {
return;
}
lastWidth = widthNow;
// Length changed, user must have zoomed, invoke listeners.
for (i = zoomListeners.length - 1; i >= 0; --i) {
zoomListeners[i]();
}
}
setInterval(pollZoomFireEvent, 100);
})();
Sounds like an application of Jquery ScrollTop and some manipulation of CSS properties of the navbar element. So for example, under certain scroll conditions the navbar element is changed from absolute positioning with calculated co-ordinates to fixed positioning.
http://api.jquery.com/scrollTop/
The effect you describe would usually start with some type of animation, like in TheDeveloper's answer. Default animations typically slide an element around by changing its position over time or fade an element in/out by changing its opacity, etc.
Getting the "bouce back" or "snap to" effect usually involves easing. All major frameworks have some form of easing available. It's all about personal preference; you can't really go wrong with any of them.
jQuery has easing plugins that you could use with the .animate() function, or you can use jQueryUI.
MooTools has easing built in to the FX class of the core library.
Yahoo's YUI also has easing built in.
If you can remember what site it was, you could always visit it again and take a look at their source to see what framework and effect was used.

jQuery check if browser support position: fixed

How do I check if browser supports position:fixed using jQuery. I assume I have to use $.support I think, but how?
Thank you for your time.
The most reliable way would be to actually feature-test it. Browser sniffing is fragile and unreliable.
I have an example of such test in CFT http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED. Note that the test should be run after document.body is loaded.
I find that mobile safari (specifically iOS 4.2 via the iOS Simulator on OSX) refuses to scroll anywhere unless you wait a few miliseconds. Hence the false positive.
I wrote a quick jquery plugin to work around it:
(function($) {
$.support.fixedPosition = function (callback) {
setTimeout(
function () {
var container = document.body;
if (document.createElement && container && container.appendChild && container.removeChild) {
var el = document.createElement('div');
if (!el.getBoundingClientRect) return null;
el.innerHTML = 'x';
el.style.cssText = 'position:fixed;top:100px;';
container.appendChild(el);
var originalHeight = container.style.height,
originalScrollTop = container.scrollTop;
container.style.height = '3000px';
container.scrollTop = 500;
var elementTop = el.getBoundingClientRect().top;
container.style.height = originalHeight;
var isSupported = !!(elementTop === 100);
container.removeChild(el);
container.scrollTop = originalScrollTop;
callback(isSupported);
}
else {
callback(null);
}
},
20
);
}
})(jQuery);
function fixedcheck () {
var fixedDiv = $('<div>').css('position', 'fixed').appendTo('body');
var pos1 = fixedDiv.offset().top;
$(window).scrollTop($(window).scrollTop() + 1);
var pos2 = fixedDiv.offset().top;
fixedDiv.remove();
return (pos1 != pos2)
}
/* Usage */
$(document).ready(function () {
if (!fixedcheck()) alert('Your browser does not support fixed position!')
});
You could check if position exists by making a code like this:
<html>
<script type="text/javascript">
test = function() {
if(!!document.getElementById("test").style.position) {
alert('true');
}
else{
alert('false');
}
}
</script>
<body>
<p id="test" onclick="test();" style="position:fixed;">Hi</p>
</body>
</html>
Since position exists in all main browser this will always return true. I imagine there isn't a way to check the possible values of position, so you'll have to check which browser and which version the user are viewing your page as Paolo Bergantino said.
position:fixed apparently works for all block elements in Mobile Safari (4.3.2) except body, so the CFT answer (http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED) should have this in it:
var isSupported = (container.scrollTop === 500 && elementTop === 100);
The feature-test Position fixed support , mentioned above, returns a false-positive on Opera Mini (which does not support position: fixed).
I've created another check if position:fixed is really supported in browser. It creates fixed div and try to scroll and check if the position of div changed.
function isPositionFixedSupported(){
var el = jQuery("<div id='fixed_test' style='position:fixed;top:1px;width:1px;height:1px;'></div>");
el.appendTo("body");
var prevScrollTop = jQuery(document).scrollTop();
var expectedResult = 1+prevScrollTop;
var scrollChanged = false;
//simulate scrolling
if (prevScrollTop === 0) {
window.scrollTo(0, 1);
expectedResult = 2;
scrollChanged = true;
}
//check position of div
suppoorted = (el.offset().top === expectedResult);
if (scrollChanged) {
window.scrollTo(0, prevScrollTop);
}
el.remove();
return suppoorted;
}
This function was tested in Firefox 22, Chrome 28, IE 7-10, Android Browser 2.3.

Categories

Resources