Entry
How can I cache images and get their sizes?
Nov 14th, 2002 19:15
Mark Filipak,
Written by Mark Filipak, 2002, and placed into the public domain.
No rights reserved.
14-11-2002: Corrected to account for 'px' units. Strip 'px' before math
and add 'px' before setting width and height.
The technique below will cache images in a hidden DIV. After the page
has loaded, the sizes of the cached images can be read.
Note: Don't let my reverse indentation put you off.
//_____ FOR CONVENIENCE, PUT ID-URI PAIRS IN A TEMPORARY ARRAY.
// NOTE THAT THE 'uri_' BITS HAVE BEEN ASSIGNED VALUES ELSEWHERE,
// BUT THEY COULD HAVE BEEN LISTED HERE AS LITERAL STRINGS.
myA = new Array
(
'binocularsIcon',uri_binocularsIcon
,'computerIcon',uri_computerIcon
,'folderClosedIcon',uri_folderClosedIcon
,'pageAudioIcon',uri_pageAudioIcon
,'pageBinaryIcon',uri_pageBinaryIcon
,'pageBlankIcon',uri_pageBlankIcon
,'pageGraphicsIcon',uri_pageGraphicsIcon
,'pageVideoIcon',uri_pageVideoIcon
,'pageTextIcon',uri_pageTextIcon
);
//_____ CREATE A DOCUMENT FRAGMENT...
myNode = document.createDocumentFragment();
//_____ ...AND PUT A HIDDEN DIV INTO IT.
myNode = myNode.appendChild(document.createElement('DIV'));
myNode.style.position = 'absolute';
myNode.style.visibility = 'hidden';
//_____ FOR EACH PAIR IN THE ARRAY, CREATE AN OBJECT ELEMENT
// CHILD IN THE HIDDEN DIV.
for (i=0; i<myA.length; i+=2)
{
myNode = myNode.appendChild(document.createElement('OBJECT'));
myNode.setAttribute('id',myA[i]);
myNode.setAttribute('data',myA[i+1]);
myNode = myNode.parentNode;
}
myNode = myNode.parentNode;
//_____ APPEND THE DIV TO THE BODY TO RENDER ITS OBJECTS.
void document.body.appendChild(myNode);
//_____ TOSS OUT THE TEMPORARY ARRAY.
delete myA;
// DONE!
Note that since the image cache is in an absolutely positioned, hidden
DIV, it doesn't show and it doesn't displace normal page flow.
Later (on a mouse click or some other event), the cached objects can be
retrieved and their sizes can be used to compute the sizes of DIVs that
they will be embedded into. For example, suppose I want to use
pageBlankIcon in a DIV on the page, and that I want to size the DIV
before embedding the icon -- I might want to do this because the icon
is buried deep inside many, many layers of DIVs and I want to compute
the sizes of all the DIVs before they are rendered. Here's what I do:
//_____ SUPPOSE MY CONSTRUCTION BEGINS WITH THIS WORK-NODE.
mySubTree = document.createDocumentFragment();
/*
FURTHER ALONG IN MY CONSTRUCTION, I REACH THE SECTION OF CODE THAT
BUILDS THE DIV INTO WHICH I WANT TO EMBED AN ICON.
*/
//_____ GET THE CACHED ICON.
O = document.getElementById('pageBlankIcon');
//_____ GET ITS COMPUTED STYLE.
CS = document.defaultView.getComputedStyle(O,'');
//_____ GET ITS WIDTH AND HEIGHT.
myIconWidth = CS.getPropertyValue('width');
myIconWidth = myIconWidth.substr(0,myIconWidth.length-2);
myIconHeight = CS.getPropertyValue('height');
myIconHeight = myIconHeight.substr(0,myIconHeight.length-2);
//_____ SIZE THE DIV IT WILL BE EMBEDDED INTO.
// SUPPOSE THE DIV ALREADY HAS SOME DIMENSIONS, FROM TEXT OR WHATNOT,
// AND SUPPOSE THAT THE ICON ADDS TO THE DIV'S WIDTH...
myDivWidth = myDiv.style.width;
myDivWidth = myDivWidth.substr(0,myDivWidth.length-2);
myDiv.style.width = String(myDivWidth+myIconWidth)+'px';
// ...BUT IT INCREASES THE HEIGHT ONLY IF IT IS TALLER.
myDivHeight = myDiv.style.height;
myDivHeight = myDivHeight.substr(0,myDivHeight.length-2);
if (myIconHeight>myDivHeight) myDiv.style.height = myIconHeight+'px';
//_____ NOW, EMBED THE IMAGE IN THE PERFECTLY-SIZED DIV.
myDiv.appendChild(O.cloneNode(true));
The DIV, myDiv, now has the OBJECT element embedded as a child, and the
OBJECT element contains the icon image. The next step pastes the DIV
into a new parent -- suppose that it is a title bar.
myTitleBar = document.createDocumentFragment();
myTitleBar = myTitleBar.appendChild(document.createElement('DIV');
void myTitleBar.appendChild(myDiv);
Later, myTitleBar will be pasted into mySubTree in the same manner, and
when mySubTree has been fully assembled, it will be pasted into the body
like this:
document.body.appendChild(mySubTree);
and whatever it is that I have built will pop into view.
The techniques above can be used to cache menus, buttons, etc. and then
build complex sub-trees on demand. The key is that by caching objects
in document body, they are rendered along with the rest of the page, so
that when they are needed they are complete and their sizes can be used
to size the elements that they will be embedded into.
Unfortunately, an OBJECT element doesn't seem to take on the size of
its content if that content is not an image (i.e., if it is HTML, for
example). I'm working on a solution. Stay tuned...
Ciao -- Mark Filipak