random memes }

Auto-adjusting sizer - Javascript

Took a couple years to get reasonably fluent at HTML/CSS/Javascript, and pick out what seems a decent programming model. Reached the point where most of the public examples of Javascript usage (in particular) look to me as very poorly written. Still it seems like every time I turn out another example, I find another useful idiom.

Take sizing of DOM elements. Say you want to put a sizable component within a page. The DOM model for this is a mess. You can set the width and height of an element o with o.style.width and o.style.height (assuming the element is sizable). You can read the width and height of an element with o.offsetWidth and o.offsetHeight. Not exactly symmetric, and in fact - due to the DOM layout model - these are in fact often not quite the same numbers. Yuck.

Looking at a bunch of repetitive code (button handlers to alter a component's width/height), realized there was an common idiom that could be extracted into a "sizer" object.

grid.sizer = function() { var o = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight }; o.apply = function() { grid1.container.style.width = o.dx + "px"; grid1.container.style.height = o.dy + "px"; o.dx = grid1.container.offsetWidth; o.dy = grid1.container.offsetHeight; } return o; };

(The component has been mapped by functions DOM.byId() and DOM.byName() described here.)

Now either width, height, or both can be set or adjusted using the "sizer".

sizer.dx = 500; sizer.apply();

sizer.dx += 100; sizer.dy = 400; sizer.apply();

Kind'a like a remote control for a component. But still not quite right, as (depending on the CSS) the actual component size may be a little different than what was set via the style. The difference is not readily deducible from the DOM, and I did not want to kludge in anything browser or style specific. The difference between the actual size and the size wanted can be computed ... then it occurred to me that the "sizer" could be made auto-adjusting.

grid.sizer = function() { var ddx = 0, ddy = 0; var o = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight }; o.apply = function() { grid1.container.style.width = (ddx + (0 | o.dx)) + "px"; grid1.container.style.height = (ddy + (0 | o.dy)) + "px"; var now = { dx: grid1.container.offsetWidth, dy: grid1.container.offsetHeight }; ddx += o.dx - now.dx; ddy += o.dy - now.dy; o.dx = now.dx; o.dy = now.dy; } o.adjust = function() { var want = grid.sizer(); o.apply(); o.dx = want.dx; o.dy = want.dy; o.apply(); } return o; }; var sizer = grid.sizer(); sizer.adjust(); grid.layout();

The essential bit here is that the "sizer" remembers the difference between the size as set, and the actual size in ddx and ddy. A single upfront call to sizer.adjust() initializes ddx and ddy. Note that on later changes to the component size, if the actual size differs from what was wanted (due to CSS changes, browsers bugs, whatever), then ddx and ddy are adjusted to match.

Simple, clean, and browser-independent.