Reputation: 1214
I'm creating my own scrollbar component using vanilla javascript. The element is resizable, so when it's resized, the scrollbar's maximum scroll position and size will follow.
Everything is fine if I resize with normal speed, but if I test quickly, the calculation result (size and max. pos) produce wrong result, I guess because of the offsetWidth
or offsetHeight
mistake.
My scrollbar's render function triggered when on resize, sbv means scrollbar vertical and sbh means scrollbar horizontal:
this.sbv.size = this.content.offsetHeight / this.content.scrollHeight;
this.sbv.style.height = (this.sbv.size * 100) + "%";
this.sbv.max = (100 - this.sbv.size * 100) / 100 * this.content.offsetHeight;
this.sbv.pos = this.content.scrollTop / this.content.scrollHeight * this.content.offsetHeight;
this.sbv.style.top = (this.sbv.pos / this.content.offsetHeight * 100) + "%";
this.sbh.size = this.content.offsetWidth / this.content.scrollWidth;
this.sbh.style.width = (this.sbh.size * 100) + "%";
this.sbh.max = (100 - this.sbh.size * 100) / 100 * this.content.offsetWidth;
this.sbh.pos = this.content.scrollLeft / this.content.scrollWidth * this.content.offsetWidth;
this.sbh.style.left = (this.sbh.pos / this.content.offsetWidth * 100) + "%";
Does anyone know why this happen? TiA
Upvotes: 1
Views: 3563
Reputation: 136746
That will be a theoretical answer only, because I didn't took the time to setup a repro case, and because "produce wrong results" is a bit broad of a description.
I suspect the problem is actually the inverse of your claim:
offsetWidth
, offsetHeight
, scrollHeight
and scrollWidth
actually give you the correct values, in a mean you may not have expected.
These properties, in order to return the correct values do trigger a recalculation of all the bounding boxes of the elements in the document, this is known as a "reflow".
This reflow implies that all the CSS rules you did set previously are now applied and active.
So in your code, when you do
elem.size = content.offsetHeight / content.scrollHeight;
elem.style.height = (elem.size * 100) + "%";
elem.max = (100 - elem.size * 100) / 100 * content.offsetHeight;
The value of content.offsetHeight
at the first line, and the one at the third line will probably not be the same anymore.
var elem = document.getElementById('elem');
var content = document.getElementById('content');
console.log('before', content.offsetHeight);
elem.size = content.offsetHeight / content.scrollHeight;
elem.style.height = (elem.size * 100) + "%";
elem.max = (100 - elem.size * 100) / 100 * content.offsetHeight;
console.log('after', content.offsetHeight);
body{
height: 100vh;
}
#content,#elem {
border: 1px solid;
}
#elem {
height: 800px;
}
<div id="content">
<div id="elem"></div>
</div>
So, not only triggering multiple reflows like that is terrible for the performances, but you will probably have your calculations wrong indeed.
The best is probably to cache all the current values before hand in variables, make all your calculations and only at the end set all the new styles.
Also note that all this would be better ran in a requestAnimationFrame
debouncer, so it happens only once per painting frame.
Upvotes: 2