Skip to content Skip to sidebar Skip to footer

Prevent Scrolling Of A Single Column In A Table

Ok wizards, someone knows how to do this ... I have a very complex custom control that renders the availability information for x number of people (could be 1 could be 100). the

Solution 1:

Here's a solution by Markku Uttula that does this with a combination of CSS and HTML (below). Works with reasonable performance even on IE6, to my surprise. V. impressive work. The scroll bars on Chrome, Opera, and Firefox end up underneath the frozen columns/rows for some reason, but hopefully the bars are big enough that that won't be a problem...

CSS:

body { margin: 0; height: 100% }
.b { border-collapse: collapse }
.b td { border: 1px solid #f80; vertical-align: top }
p { margin: 1px; padding: 0; font-size: 9px; font-family: Arial }
p.c { text-align: center; font-size: 12px }
p span { font-size: 12px; white-space:nowrap; color: #888 }
/* TopLeft fixed corner */
td.bg101 { background-color: #ffc }
/* Top fixed rows */
td.bg000 { background-color: #f8c }
td.bg001 { background-color: #f4c }
/* Left fixed rows */
td.bg110 { background-color: #fcc }
td.bg111 { background-color: #fcf }
/* The scrollable content area */
td.bg010 { background-color: #fff }
td.bg011 { background-color: #ff4 }

JavaScript:

var ieBug = ((document.all) && (!window.opera)); // IE doesn't really handle fixed elements well :)
var fpElement = false;
function fpCW() { return document.documentElement ? document.documentElement.clientWidth : document.clientWidth; }
function fpCH() { return document.documentElement ? document.documentElement.clientHeight : document.clientHeight; }
function fpTopLeft(elementNode, iTop, iLeft) {
    elementNode.style.top = (iTop)+"px";
    elementNode.style.left = (iLeft)+"px";
}
function fpDefaults(elementNode) {
    elementNode.style.position = ieBug ? "absolute" : "fixed";
    fpTopLeft(elementNode,0,0);
    return elementNode;
}
function fpClone(elementNode, cloneZindex) {
    var clone = fpDefaults(elementNode.cloneNode(true));
    clone.style.background = "#fff";
    clone.style.overflow = "hidden";
    clone.style.border = "none";
    clone.style.zIndex = cloneZindex;
    return clone;
}
function fpInitFreezePanes(fpTableElementId, fpTableContainerDivElementId, fpPivotCellId, fpDisableBodyScrollbars) {
    if (fpElement !== false) {
      alert("Page already contains an element with fixed panes.");
    } else {
        var fpTableElement = document.getElementById(fpTableElementId);
        var fpTableContainerDivElement = document.getElementById(fpTableContainerDivElementId);
        var fpPivotCell = document.getElementById(fpPivotCellId);
        if ((!fpTableElement) || (!fpTableContainerDivElement) || (!fpPivotCell)) {
            alert("Unable to find table, container or pivoting cell?");
        } else {
            fpElement = fpTableContainerDivElement;
            if (fpDisableBodyScrollbars) {
                var x = document.getElementsByTagName("body")[0];
                x.style.overflow = "hidden";
                x = document.getElementsByTagName("html")[0];
                x.style.overflow = "hidden";
            }
            fpTableContainerDivElement = fpDefaults(fpTableContainerDivElement);
            fpTableContainerDivElement.style.width = (fpCW()-2)+"px";
            fpTableContainerDivElement.style.height = (fpCH()-2)+"px";
            fpTableContainerDivElement.fpPT = fpPivotCell.offsetTop;
            fpTableContainerDivElement.fpPL = fpPivotCell.offsetLeft;

            var copy1 = fpClone(fpTableContainerDivElement, 4);
            copy1.style.width = (fpTableContainerDivElement.fpPL)+"px";
            copy1.style.height = (fpTableContainerDivElement.fpPT)+"px";
            var copy2 = fpClone(fpTableContainerDivElement, 3);
            copy2.style.width = (fpTableElement.offsetWidth)+"px";
            copy2.style.height = (fpTableContainerDivElement.fpPT)+"px";
            var copy3 = fpClone(fpTableContainerDivElement, 2);
            copy3.style.width = (fpTableContainerDivElement.fpPL)+"px";
            copy3.style.height = (fpTableElement.offsetHeight)+"px";

            fpTableContainerDivElement.style.zIndex = 1;
            fpTableContainerDivElement.appendChild(copy1);
            fpTableContainerDivElement.copy1 = copy1;
            fpTableContainerDivElement.appendChild(copy2);
            fpTableContainerDivElement.copy2 = copy2;
            fpTableContainerDivElement.appendChild(copy3);
            fpTableContainerDivElement.copy3 = copy3;
            fpTableContainerDivElement.repositioning = false;
            fpTableContainerDivElement.oldST = -1;
            fpTableContainerDivElement.oldSL = -1;
            fpTableContainerDivElement.style.overflow = "scroll";

            /* JUST SOME STYLING HERE */
            copy1.style.borderRight = "1px dashed #000";
            copy1.style.borderCollapse = "collapse";
            copy2.style.borderBottom = "1px dashed #000";
            copy2.style.borderCollapse = "collapse";
            copy3.style.borderRight = "1px dashed #000";
            copy3.style.borderCollapse = "collapse";
            /* JUST SOME STYLING HERE */

            // if (ieBug) {
            if (fpTableContainerDivElement.style.setExpression) {
                function fpIeBugFix(elementNode, containerNodeId, arrayOfExpressionsToSet) {
                    for (var i in arrayOfExpressionsToSet) {
                        if (arrayOfExpressionsToSet[i][2]) {
                            elementNode.style.setExpression(arrayOfExpressionsToSet[i][0], arrayOfExpressionsToSet[i][1]+"document.getElementById('"+containerNodeId+"')."+arrayOfExpressionsToSet[i][2]+"+'px'");
                        } else {
                            elementNode.style.setExpression(arrayOfExpressionsToSet[i][0], arrayOfExpressionsToSet[i][1]);
                        }
                    }
                }
                fpIeBugFix(fpTableContainerDivElement.copy3, fpTableContainerDivElementId, [["top","","scrollTop"],["left","","scrollLeft"],["marginTop","-","scrollTop"]]);
                fpIeBugFix(fpTableContainerDivElement.copy2, fpTableContainerDivElementId, [["top","","scrollTop"],["left","","scrollLeft"],["marginLeft","-","scrollLeft"]]);
                fpIeBugFix(fpTableContainerDivElement.copy1, fpTableContainerDivElementId, [["top","","scrollTop"],["left","","scrollLeft"]]);
                fpIeBugFix(fpTableContainerDivElement, fpTableContainerDivElementId, [["width","fpCW()+'px'"],["height","fpCH()+'px'"]]);
                document.recalc(true);
            } else {
                fpElement.onmousemove = fpElement.onscroll = function() {
                    if (!this.repositioning) {
                        this.repositioning = true;
                        var st = this.scrollTop;
                        var sl = this.scrollLeft;
                        if ((this.oldST != st) || (this.oldSL != sl)) {
                            this.oldST = st;
                            this.oldSL = sl;
                            // this.copy3.previousDisplay = this.copy3.style.display;
                            // this.copy3.style.display = "none";
                            this.copy3.style.marginTop = "-"+(st)+"px";
                            // this.copy3.style.display = this.copy3.previousDisplay;
                            // this.copy2.previousDisplay = this.copy2.style.display;
                            // this.copy2.style.display = "none";
                            this.copy2.style.marginLeft = "-"+(sl)+"px";
                            // this.copy2.style.display = this.copy2.previousDisplay;
                        }
                        fpElement.repositioning = false;
                    }
                };
                window.onresize = function() {
                    fpElement.style.width = fpCW()+"px";
                    fpElement.style.height = fpCH()+"px";
                }
            }
        }
    }
}

Solution 2:

Ok so based on the table above that i already have i had to assume the fixed column would have a fixed width but i managed to do this ...

<div style="position:relative;">
    <div style="width:100%; overflow:scroll; height:400px;">

<table style="margin-left:100px;">
  <tr><td style="position:absolute;"></td><td colspan='31'>Jan</td><td colspan='28'>feb</td>...</tr>
  <tr><td style="position:absolute;"></td><td>1</td><td>2</td><td>3</td> ...<tr/>
  <tr><td style="position:absolute;">Person 1</td><td></td><td></td><td>Busy</td> ...<tr/>
  <tr><td style="position:absolute;">Person 2</td><td></td><td>Busy</td><td>Busy</td> ...<tr/>
</table>

    </div>
</div>

this results in the marked cells taking an absolute position on the left and the table remains scrollable ... A little work is required to clean it up and make it look pretty but essentially my code when extracted out looks like this ...

<style type="text/css">
    .Calendar                   { max-height:400px; border-collapse:collapse; table-layout:fixed; margin-left:105px; }
    .Calendar .Resources  { width:100px; position:absolute; left:0px; background:white; }
    .Calendar tr                { vertical-align:top; }
    .Calendar td               { border:solid 1px black; vertical-align:top; padding:2px; border:solid 1px  #EFEFEF; }
    .Calendar .day            { background:#FFFBD6; border:solid 1px #FEEFB3; }
    .Calendar .dayWithEvent   { background:white; border:solid 1px #FEEFB3; border-top:solid 1px blue; }
</style>

<div style="position:relative;">
    <div style="width:100%; overflow:scroll; height:400px;">
        <table class="Calendar">
            <asp:Repeater ID="ui_calMonths" runat="server" onitemdatabound="ui_calMonths_ItemDataBound">
                    <HeaderTemplate>
                        <tr><td class="Resources" style="background:white; border:none;">&nbsp;</td>
                    </HeaderTemplate>
                    <ItemTemplate>
                        <td colspan='<%# ((Month)Container.DataItem).Days.Length %>'><%# Eval("Name") %></td>
                    </ItemTemplate>
                    <FooterTemplate>
                        </tr>
                        <tr><td class="Resources" style="background:white; border:none;">&nbsp;</td>
                            <asp:Repeater ID="ui_calDays" runat="server">
                                <ItemTemplate>
                                    <td><%# ((DateTime)Container.DataItem).ToString("dd") %></td>
                                </ItemTemplate>
                            </asp:Repeater>
                        </tr>
                    </FooterTemplate>
                </asp:Repeater> 
            <asp:Repeater ID="ui_grdResources" runat="server">
                <HeaderTemplate><tr></HeaderTemplate>
                <ItemTemplate>
                    <td class="Resources"><%# Eval("Text") %></td>
                    <%# ResourceEvents(((RowContext)Container.DataItem)) %>
                </ItemTemplate>
                <FooterTemplate></tr></FooterTemplate>
            </asp:Repeater>
        </table>    
    </div>
</div>

basically the codebehind figures out how many cells to render in each row (the repeaters are fundamental in this working but the result is essentially a nicely formatted table which contains the month names for the given date range in the first row, the days for those months in the second and in the third a fixed column containing the names and the dynamically populated from my internal API calendar items ... very cool !!!


Solution 3:

get the jquery datatables fixedcolumns plugin

http://datatables.net/extras/fixedcolumns/


Post a Comment for "Prevent Scrolling Of A Single Column In A Table"