Is there a simple way in jQuery to detect when scrollbars appear and disappear on a div that has overflow:auto? (Like an event? Fingers crossed...)
(I'd prefer not to have to look at the height of the content of the div)
Another way to achieve this is to check whether there are scrollbars present using scrollLeft or scrollTop:
//nudge the scrollbar away from its starting position
$('#your_selector').scrollLeft(1);
//A value of 0 is assigned if the scrollbars are at their default position,
//or are abscent
if($('#your_selector').scrollLeft() !== 0) return true;
//put the scrollbar back to its starting position
$('#your_selector').scrollLeft(0);
As others have said, there is no easy way. Here's some code I've used in the past to detect if a scrollbar is present.
// Used like $('#my-id').hasScrollbar();
jQuery.fn.hasScrollbar = function() {
var scrollHeight = this.get(0).scrollHeight;
//safari's scrollHeight includes padding
if ($.browser.safari)
scrollHeight -= parseInt(this.css('padding-top')) + parseInt(this.css('padding-bottom'));
if (this.height() < scrollHeight)
return true;
else
return false;
}
You'll manually need to call this after adding or removing content from the div and it probably will only work if you call it on visible elements, but it's better than starting from scratch.
As far as I know, there is not event for that.
However, you "could" write your own special event for that, I guess you have to check
for the height and width.
It should be possible to detect scrollbars if the .innerHeight exceds the .outerHeight
value of an element.
Related
I'm trying to create my own input+dropdown control from scratch (in vue.js, though not relevant). I want to use mouse or keyboard for scrolling down the list of items.
For that I'm using a div with a fixed height and overflow-y and in that div, for each item, I use another div. When scrolling with the keyboard, I keep track of the selected item and use that to set the scrollTop position of the div, so that the scroll bar moves with the keyboard input and the selected item stays visible in the middle of the div. Here is the sample in fiddle https://jsfiddle.net/ce6k2a3j/11/
But the part I'm having issues with is setting the .scrollTop property when there are a lot of items in the list and there is some kind of scaling.
setScrollPosition () {
if(+this.keyIndex >= 6){
this.$refs.testMainDiv.scrollTop = (+this.keyIndex - 6) * +this.$refs.testItemDiv[+this.keyIndex].clientHeight;
}
else{
this.$refs.testMainDiv.scrollTop = 0;
}
},
My problem is that, in Windows 10, if I change the scaling of my display to 125% (since I use a 4k monitor), scrolling all the way down the list will move the selected item slightly up each time the key.down fires. Is there a way to make this scale proof ? It also happens when using page zoom.
this is what happens:
In case of transforms, the offsetWidth and offsetHeight returns the element's layout width and height, while getBoundingClientRect() returns the rendering width and height.
MDN: Determining the dimensions of elements
So, in this case clientHeight has the same result as offsetHeight. You neeed to use getBoundingClientRect().
setScrollPosition () {
if(+this.keyIndex >= 6){
this.$refs.testMainDiv.scrollTop = (+this.keyIndex - 6) * this.$refs.testItemDiv[+this.keyIndex].getBoundingClientRect().height;
}
else{
this.$refs.testMainDiv.scrollTop = 0;
}
}
I tested it with the browser scaling and it works, you try it with your display scaling and let's see.
I need to know if the end of a div element is currently visible in the users' browser.
I tried something I saw on the web, but scrollTop() always gave me zero in my Browser. I read something about an issue in Chrome, but I didn't understand quite well.
jQuery(
function($) {
$('#flux').bind('scroll', function() {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
alert('end reached');
}
})
}
);
My idea is the following:
1- User loads page and sees a Bar (sticky div at bottom visible page) with some information.
2- After scrolling a bit, and reaching the end of a div element, this bar will position there, after the div. This is the bar's original position
I wasn't really able to know when I was at the end of the div element. Eventually I found this code:
if ($(window).scrollTop() >= $('#block-homepagegrid').offset().top + $('#block-homepagegrid').outerHeight() - window.innerHeight) {
$('.hero-special-message').removeClass('hero-special-messege-scrolling');
} else {
$('.hero-special-message').addClass('hero-special-messege-scrolling');
}
});
I see that it's working, but I'm having a bit of trouble understanding what it does.
I know the following:
1. $(window).scrollTop();
this gives me the amount of pixels the user has scrolled, pretty self explanatory.
2. $('#block-homepagegrid').offset().top;
I THINK this is the distance between the start of the page and the start of the div. I know it's the current coordinates, but what is top exactly here?
3. $('#block-homepagegrid').outerHeight();
this gives the height of the element, I know there are 3, like
height(), innerHeight() and outerHeight(), if you want to take into
account border, margin, padding, which is the better to use?
4. window.innerHeight;
I understand this is what the user sees, but I'm having troubles understanding why does it matter for my situation.
Thanks!
You may be interested in the native JavaScript IntersectionObserver API. It automatically figures out what percentage of a given element is visible in the window and triggers callbacks based on that. So then you can do this:
function visibleHandler(entries) {
if (entries[0].intersectionRatio >= 1.0) {
// The whole element is visible!
} else {
// Part of it is scrolled offscreen!
}
}
const observer = new IntersectionObserver(visibleHandler, {threshold: 1.0});
observer.observe(document.getElementById('flux'));
Now, whenever the element with ID flux is 100% in view, it will trigger the visibleHandler. It will also trigger again if it's scrolled back out of view; that's why the function checks the ratio of visibility to see if it just hit 100% or just got reduced from 100%. You could be more fancy and use the observer entry's insersectionRect, which gives you the rectangle containing the visible portion of the element, and use that to determine top/bottom visibility.
Is there a way to use javascript to detect if html content can't fit the screen (is scrollable) to show a "back to top button" only if necessary?
If you're happy to use jQuery, you could try finding the window height and comparing it to a wrapper element height. If the element height is greater, show the button.
var x = $(window).height();
if ($('#test').height() > x) {
alert('scrollable');
//add in button here
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test" style="height:2000px;background:silver"></div>
Hard to tell, but perhaps you could get the element using document.getElementById(), then use innerHeight (http://www.w3schools.com/jsref/prop_win_innerheight.asp) to determine if the element exceeds the height.
Edit - seems like I might have misread a bit. If you want to check the entire HTML content on the page, you can use something like scrollHeight (https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight) on document.
You can compare the height of the page to the height of your browser:
document.body.offsetHeight < screen.availHeight
That will return true if the page fits. Although for your purpose, why not just show the button if the user is scrolled down at all? Assuming you can use jQuery,
$(window).scroll(function(){
if ($(document.body).scrollTop() > 0)
{
//Add or show button here
}
})
It's not IE supported, but my fave vanilla javascript is always:
var scrollHeight = window.scrollY;
console.log(scrollHeight);
in javascript can I make sure that my large div scroll vertically
only in chunks of (let's say) 16 pixels
In java, those are called 'units of increment'.
I can't find anything similar in javascript:
I want to ensure that a certain area (div) when partially scrolled is always a multiple of 16 the view.
That allows me to do tricks with background images and others.
thanks
var lastScroll = 0;
$('div').scroll(function(){
var el = $(this),
scroll = el.scrollTop(),
round = lastScroll < scroll ? Math.ceil : Math.floor;
lastScroll = round(scroll/16) * 16;
el.scrollTop(lastScroll);
});
http://jsfiddle.net/m9DQR/2/
Ensures scrolls are done in multiples of 16 pixels. You can easily extend this to be a plugin that allows for a variable amount (not a fixed, magical 16).
Yes, this is possible, but it will require using javascript to capture the scroll event and then manipulate it. This script (sorry jQuery is what I had) and overrides the scroll event. It then replaces it with the exact same scroll distance. You could perform your own math to adjust the value of scrollTo. We have to check both mousewheel and DOMMouseScroll events because the first is not supported by FF. This doesn't seem to apply in your case, but a user may have the number of lines to scroll set to something other than the default three. So the if statement calculates the distance. I left it in there though in case other people stumble on this question and it is important to them though.
$('body').bind('mousewheel DOMMouseScroll', function(e) {
var scrollTo = null;
if (e.type == 'mousewheel') {
scrollTo = (e.wheelDelta * -1);
}
else if (e.type == 'DOMMouseScroll') {
scrollTo = 40 * e.detail;
}
//adjust value of scrollTo here if you like.
scrollTo = 16;
if (scrollTo) {
e.preventDefault();
$(this).scrollTop(scrollTo + $(this).scrollTop());
}
});
Coming from another programming language I also found JavaScript difficult when dealing with UI. In your case I would just set a handler to the event onscroll and query the position of the div relative to the scroll position. Return false whenever position of div is not divisible by 16px and create a counter to allow reposition after another 16px is scrolled.
Here's what i have so far:
function loadOff(){
$(document).ready(function(){
$("#eLoader").ajaxStop(function(){
$(this).hide();
$("#eventsContent").show();
var h = document.body.scrollHeight;
$("#bodyBackground").css("height",h+100+"px");
$("#sidePanel1").css("height",h-105+100+"px");
$("#bottom").css("top",h+100+"px");
});
});
}
This is a callback function for a JQuery ajax function, basically what is does is when all ajax is finished .ajaxStop() it hides the loader then shows the content.
The problem i am having is adjusting bodyBackground, sidePanel, and bottom to fit the content. I dont care to have it elastic and retract for short content at this point, i would just like it to extend to proper positioning based on content length.
All divs are absolutely positioned. The numbers in the function are broken down simply to make it easy to explain. -105 is the offsetTop of that element and +100 is the margin between the end of the content and the elements.
if there is a better, more efficient way to achieve this outcome, please, do tell.
Thanks.
Based on your code, the only thing you ought to see is the top 105px of #sidePanel1. Is that your intent? (h = the bottom of the window, according to your code.)
Sticking with the JQuery patterns, you would use
var h = $(window).height();
Maybe you're looking for this instead of the browser window's height? It will get the height of the content element.
$("#eventsContent").outerHeight();