The difference between attributes and properties
does not look very clear from the JavaScript point of view.
The property textField.value
delivers the current user input,
while textField.getAttribute('value')
delivers just the initial value?
On the Internet you find answers:
An HTML attribute is one item in the "attributes" property of the DOM-node.
Attributes and properties are connected in different ways. Find details in the w3c specification.
Similar it is for read/writable style properties like height
and their read-only reflection offsetHeight
or clientHeight
.
If you set style.height
of an DOM element, that value will
be reflected into offsetHeight
as soon as you request that property.
With one exception: there was an animation (transition) defined on that property.
This Blog is about what happens then.
Example
Set "Height" to 8 em, and then press "Set Height". This will trigger
grayRectangle.style.transition = ""; grayRectangle.style.height = 8+"em";
By the control output inside the gray rectangle you can see that
grayRectangle.offsetHeight
is set to the according pixel-height immediately.
Now set "Height" to 12 em, and then press "Animate Height". This will do
grayRectangle.style.transition = "height 2s"; grayRectangle.style.height = 12+"em";
A timer is used to track the changes in grayRectangle.offsetHeight
.
You see that the CSS height
value is not immediately reflected into that
DOM node property, instead it is subsequently updated by the animation.
Consequences
When you use animation on a layout property like
width
or height
,
CSS style requirements differ from DOM reality for a while.
A geometry calculation done in that time will not be accurate
when it uses DOM properties like offsetWidth
or offsetHeight
.
So be warned when you use geometry calculations like I introduced in my Blog about get and set on element width / height. They won't work when you use animations.
Workaround
In such a case you need to buffer all affected elements,
set and get values in a custom-property on them,
and provide a flush()
function that you call at end of all layout calculations.
Here is an example of what I mean.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | var bufferedHeights = []; var getOverallHeight = function(element) { if (element.deferredHeight === undefined) { element.deferredHeight = elementDimensions.getOverallHeight(element); addToBufferedHeights(element, false); } return element.deferredHeight; }; var setOverallHeight = function(element, numericHeightValue) { element.deferredHeight = numericHeightValue; addToBufferedHeights(element, true); }; var addToBufferedHeights = function(element, modified) { for (var i = 0; i < bufferedHeights.length; i++) { if (bufferedHeights[i].element === element) { if (modified) /* modification overwrites read-only access */ bufferedHeights[i].modified = modified; return; /* already in list */ } } bufferedHeights.push({ element: element, modified: modified }); }; var flushBufferedHeights = function() { for (var i = 0; i < bufferedHeights.length; i++) { var bufferedHeight = bufferedHeights[i]; if (bufferedHeight.modified) elementDimensions.setOverallHeight(bufferedHeight.element, bufferedHeight.element.deferredHeight); bufferedHeight.element.deferredHeight = undefined; } bufferedHeights = []; }; |
You can use getOverallHeight()
and setOverallHeight()
all the time during layout calculations.
When all elements are sized, call flushBufferedHeights()
to close the gap between CSS requirements and DOM reality.
That way you work around both calculation mistakes and
repaint / reflow
problems.