So I've been looking at a recent problem and trying to come up with an elegant solution. I'll need to give a little background. I found a legacy component that had originally been coded using HTML 4.0 and seemed to work fine. Recently, we migrated to the XHTML1.0 standard, and the compoent started breaking the DOM. The component was an ASP.NET DataGrid inside of a div inside of another wrapper div that served as the components main container. The component was designed to have the scrolling functionality of the DataGrid (without the scrollbar using custom buttons instead) but was supposed to be able to display only a small subset of the DataGridItems at a time. For the purposes of this post, lets just say 5. The component was ALSO supposed to have a button/link that would trigger all of the items in the list to become visible... oh and paging was not allowed.
The first developer that worked on this problem came up with a good solution using HTML4.0, but once the document standard changed, the component began to break the DOM. Here's how they approached the issue. The main wrapper div contained another div that acted as a viewport for the DataGrid by setting the divs overflow property to "hidden" and then having a explicit size declared for the div in CSS. When the user triggered the "show all" button, the component was passed to a javascript function that would check the value of the overflow property and then switch it to either visible or hidden based on the components previous overflow property. Pretty ingenious approach.
So here's a little code:
CSS
.myCollapsiblePanel
{
height: 300px;
overflow: hidden;
}
JAVASCRIPT
function ExpandCollapse(control)
{
if(!control)return false;
var obj = control;
if(control.style == null) obj = document.getElementById(control);
var Overflow = obj.style.overflow;
if(Overflow == 'hidden'){
Overflow = 'visible';
}else{
Overflow = 'hidden';
}
}
Under the HTML4.0 specification this worked just fine for the viewport, however, as I mentioned before, upon changing to XHTML1.0, the contents of the viewport began to spill over the component's container. After trying to solve the issue unsuccessfully using several approaches, I realized this morning that the main problem was that the viewport overflow property change was not updating the page... in other words, the parent wrapper had no idea a change had even occurred. This morning, I finally realized that the solution was to avoid using the overflow property as the main means of toggling the component and instead use the viewports height instead.
I appended the name of the existing CSS class to .myCollapsiblePanel_Closed, copied it and created another CSS class names .myCollapsiblePanel_Open which were identical except for the height property. In the closed class, the height was declaritively set for the viewport, ensuring that only a certain subset of the datagriditems were visible. In the open class, I set the height property to height: auto; to allow the viewport to affect its changes to the DOM and cause its parent to be redrawn. Overflow was still set to hidden in both classes.
My final javascript product looked like this:
function ExpandCollapse(control)
{
if(!control)return false;
var obj = control;
if(control.style == null) obj = document.getElementById(control);
var regEx = RegExp("_Closed");
var currentTag = obj.className;
var baseTag = currentTag.split('_');
if(regEx.text(currentTag)){
obj.className = baseTagName[0] + '_Open';
}else{
obj.className = baseTagName[0] + '_Closed';
}
}
So let me explain whats happening here. by creating 2 different css classes with a delimiter "_", and then differentiating the state of the component based upon the ending portion of the className, I was able to search use a regular expression to test the className for "_Closed" and if whatever component passed in had that suffix, I could use the beginning portion of the className with a new suffix. This causes the class of the viewport to be redrawn and since there is an actual redraw triggered, the parent container redraws as well to accomodate its child. This approach allows me to reuse this function for any number of components as long as they obey the same naming convention. Tested, worked, end of story.