简体   繁体   中英

JavaScript's offsetWidth and offsetHeight give wrong value when element is resizing quickly

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM