scrollHeight property only updates when changed content is taller - javascript

I'm observing strange behaviour with scrollHeight (or perhaps I misunderstand exactly how/when it gets updated). I noticed that when replacing content inside an "overflow:hidden" element and attempting to explicitly set its height via the scrollHeight property (so I can animate with CSS) it only adjusts to the correct height when the replaced content is taller than before. Replacing the contents with shorter (or less) content, the scrollHeight property of the element stays at the larger value. I've outlined it in a simple test below in jQuery. I'm observing this in Safari, Chrome and Firefox.
Why is this happening?
$(window).resize(function() {
$cart = $('#cart')
$cart.css('height', $cart.prop('scrollHeight'))
})

Related

Why does $(window).width() change while the page is loading?

I noticed that my scripts were setting widths incorrectly, so I tried the following snippet:
var prev;
setInterval(function(){
if($(window).width()!=prev)
console.log(prev=$(window).width());
},1);
This printed 2 different values: 1464 and 1481. Since these are 17px apart, I'm almost certain this is caused by scrollbars. The second value is the correct value.
Why does $(window).width() change without resizing the window? Shouldn't it return the browser window's width, which should be constant?
$(window).width() returns the width of the window object excluding scrollbars (otherwise known as the viewport width). Depending on the operating system and browser, the vertical scrollbar can often subtract from the viewport width. This means you should call this function once all your content has been loaded, and you'll need to call it again if the content changes.
Putting this in the $.ready() function won't guarantee that you'll get the correct width with respect to the final page content. This is because $.ready fires when the DOM is loaded, but there might still be images/fonts that can affect the layout. It's very possible that a scrollbar can get added after you call $.ready(). The easiest solution to this problem is to place your call inside the $(window).load() function instead, as this fires when all content has been loaded, and there's nothing more to render.
Generally speaking, it's a good idea to set the width on DOM ready, window load, window resize, and any time the content changes. This can be done like so:
$(function() {
var window_width;
function set_window_width() {
window_width = $(window).width();
// do something with window_width
}
set_window_width(); // set width on DOM ready
$(window).on('load resize', set_window_width); // set width on window load and resize
function custom_load_content() {
// load / change content
set_window_width();
}
});
Most (if not all) of the time, the viewport width is what you want. You're probably using the width to perform some calculations and/or resize some elements, and this should always be relative to the viewport, because your CSS is relative to the viewport. But, if for some reason you want to get the width including scrollbars, you can use window.innerWidth instead (source).
$(window).width() is affected by the margin, border and padding.
These can change as the DOM is being loaded.
As mentioned above, make sure you are waiting until $(document).ready() before you start looking at / twiddling with the DOM objects.
More info here http://api.jquery.com/width/
In most of the cases, the window width changes with the content, so there must be some ajax call coming which changes some content of the window. You can use browser debug tool and open the network traffic window to see what comes when the window width changes, debug into the javascript file to find which part of window changes with that ajax call, this may help you to find the reason.

How to get updated width of content after a css change?

I'm trying to modify the css class of my body element.
Before modifying the class, I check the scroll width of my content:
$(window.document.body).prop('scrollWidth'); // 800px
Now I modify the css class and check the scroll width again:
$(window.document.body).prop('class', someCssClassName);
$(window.document.body).prop('scrollWidth'); // still reports 800px
I know the scroll width should not be 800px after this particular change. I start a timer and keep printing the scroll width, and after a few ms I see it change to 600px.
So it seems like I can't immediately get the updated content width (or I'm misinterpreting what's going on).
Is there a way to get notified when the re-flow is complete, so that I might get the updated width?
I don't want to set a timer and keep checking, if possible.
I'm trying this in an android WebView.. browser.. . So I'm not sure if this behavior will be the same if I try in a desktop browser.
Thank you
To answer the question: accessing the scrollWidth property automatically flushes any style change (forces a reflow) and then returns the computed value. This happens synchronously, hence you don't need to "wait" for the reflow to complete -- the JS will simply freeze while the reflow happens and then return the correct scrollWidth value.
You are actually facing a very specific Blink/WebKit bug in their scrollWidth implementation regarding the body element.
I've simplified your code a bit by removing some unnecessary jQuery abstraction (fiddle):
document.body.className = 'w600px';
console.log(
document.body.scrollWidth, // Firefox: 600, Chrome: viewport width
$(document.body).width() // 600 in both browsers
);
.w600px {
width: 600px;
}
From the CSSOM element.scrollWidth spec:
3. If the element is the HTML body element, the Document is in quirks mode and the element has no associated scrolling box, return max(viewport scrolling area width, viewport width).
It seems like Chrome is not checking whether the document is in Quirks mode and returning the viewport (scrolling) width independent of the document mode.
You should open a Chromium issue in these cases.
Workarounds
It really depends on your use case. $(document.body).width() is usually fine, unless the content overflows the body element's width.
Alternatively, wrap all the page's contents inside of a div and use it to apply the class and to retrieve the scrollWidth from.
Try
function getprop( el, prop ) {
var props = window.getComputedStyle (
$(el).get(0)).getPropertyValue(prop);
return String(props);
};
console.log(getprop("body", "width"))
http://jsfiddle.net/guest271314/6uKH6/5/

I want to compare between the height of scrollbar and the document height

What I want is, an example when passing the mouse on element in page, then forced to increase the height of scrollbar, in that case, I want to shows alert box.
I have used the following code
if(document.body.offsetHeight < document.body.scrollHeight ){
alert('not Equal')
}
Also I have used clientHeight function Instead of offsetHeight function,
But he does not work well except only in chrome and safari browsers.
Update....
You can not determine if the page has scrollbars using document.body, use an div container instead, it must have overflow css property and specified height and / or width. After this you can use .offsetHeight and .scrollHeight to dermine if the content is bigger than the size of the container.

Getting div height

Firebug tells me the computed style of my_div:
width 300px
height 453.167px
Yet when I execute console.log(mydiv.style.height), it gives me an empty string, even though console.log(mydiv) logs the correct element. I am sure the page has loaded by the time this logging code is called. I'd appreciate a solution that does not use jQuery.
Depending on the browser of choice, one of these will do:
mydiv.offsetHeight
mydiv.clientHeight
Get full height of a clipped DIV
Getting the height of a div
UPDATE:
Many browser inconsistencies have been fixed since my original answer. Now the clientHeight property of a DOM element is reliable.
var height = element.clientHeight;
The Element.clientHeight read-only property is zero for elements with no CSS or inline layout boxes, otherwise it's the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin.
clientHeight can be calculated as CSS height + CSS padding - height of horizontal scrollbar (if present).
Note: This property will round the value to an integer. If you need a fractional value, use element.getBoundingClientRect().
Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight
Original answer:
If you use the jQuery JS library, you can just do this:
var computed_height = $('#my_div').height();
If you use the Prototype JS library, it's similar:
var computed_height = $('my_div').getHeight();
Using a library is often the easiest & most cross-browser way to do something. Getting computed styles with vanilla js is unreliable because the properties are different across browsers.

Scrolling Overflowed DIVs with JavaScript

I've got a div that uses overflow:auto to keep the contents inside the div as it is resized and dragged around the page. I'm using some ajax to retrieve lines of text from the server, then append them to the end of the div, so the content is growing downwards. Every time this happens, I'd like to use JS to scroll the div to the bottom so the most recently added content is visible, similar to the way a chat room or command line console would work.
So far I've been using this snippet to do it (I'm also using jQuery, hence the $() function):
$("#thediv").scrollTop = $("#thediv").scrollHeight;
However it's been giving me inconsistent results. Sometimes it works, sometimes not, and it completely ceases to work if the user ever resizes the div or moves the scroll bar manually.
The target browser is Firefox 3, and it's being deployed in a controlled environment so it doesn't need to work in IE at all.
Any ideas guys? This one's got me stumped. Thanks!
scrollHeight should be the total height of content. scrollTop specifies the pixel offset into that content to be displayed at the top of the element's client area.
So you really want (still using jQuery):
$("#thediv").each( function()
{
// certain browsers have a bug such that scrollHeight is too small
// when content does not fill the client area of the element
var scrollHeight = Math.max(this.scrollHeight, this.clientHeight);
this.scrollTop = scrollHeight - this.clientHeight;
});
...which will set the scroll offset to the last clientHeight worth of content.
scrollIntoView
The scrollIntoView method scrolls the element into view.
Using a loop to iterate over a jQuery of one element is quite inefficient. When selecting an ID, you can just retrieve the first and unique element of the jQuery using get() or the [] notation.
var div = $("#thediv")[0];
// certain browsers have a bug such that scrollHeight is too small
// when content does not fill the client area of the element
var scrollHeight = Math.max(div.scrollHeight, div.clientHeight);
div.scrollTop = scrollHeight - div.clientHeight;
$("#thediv").scrollTop($("#thediv")[0].scrollHeight);
It can be done in plain JS. The trick is to set scrollTop to a value equal or greater than the total height of the element (scrollHeight):
const theDiv = document.querySelector('#thediv');
theDiv.scrollTop = Math.pow(10, 10);
From MDN:
If set to a value greater than the maximum available for the element,
scrollTop settles itself to the maximum value.
While the value of Math.pow(10, 10) did the trick using a too high value like Infintiy or Number.MAX_VALUE will reset scrollTop to 0 (Firefox 66).
I had a div wrapping 3 divs that were floating left, and whose contents were being resized. It helps to turn funky-colored borders/background on for the div-wrapper when you try to resolve this. The problem was that the resized div-content was overflowing outside the div-wrapper (and bled to underneath the area of content below the wrapper).
Resolved by using #Shog9's answer above. As applied to my situation, this was the HTML layout:
<div id="div-wrapper">
<div class="left-div"></div>
<div id="div-content" class="middle-div">
Some short/sweet content that will be elongated by Jquery.
</div>
<div class="right-div"></div>
</div>
This was the my jQuery to resize the div-wrapper:
<script>
$("#div-content").text("a very long string of text that will overflow beyond the width/height of the div-content");
//now I need to resize the div...
var contentHeight = $('#div-content').prop('scrollHeight')
$("#div-wrapper").height(contentHeight);
</script>
To note, $('#div-content').prop('scrollHeight') produces the height that the wrapper needs to resize to. Also I am unaware of any other way to obtain the scrollHeight an actual jQuery function; Neither of $('#div-content').scrollTop() and $('#div-content').height would produce the real content-height values. Hope this helps someone out there!

Categories

Resources