let's say have 20 columns total.
I want to fix till 5 columns. (means only important ID, etc will be fixed while doing horizonal scrolling)
How can I achieve it in Visual Force Datatable ?
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It only takes a minute to sign up.
Sign up to join this communityYou need to basically duplicate the table and populate with the first 5 columns (if you want them to be fixed in your case) and then show/hide the table with CSS/Javascript when the horizontal scroll reaches the beginning of the original table.
Let's say this is your original table (I've copied code from one of my previous solutions):
<apex:pageBlockTable id="table" headerClass="headerRow" first="" value="{!tempReservations}" var="tmp" width="100%" columns="26">
<apex:facet name="caption" >
<apex:outputText value="{!opportunityName}"/>
</apex:facet>
<apex:column id="no" headerValue="#" headerClass="alignCenter">
<apex:outputText rendered="{!tmp.index != 0}" value="{!tmp.index}." />
</apex:column>
<apex:column id="centreName" headerValue="Centre Name" style="font-weight: bold; white-space: nowrap;">
<apex:outputField value="{!tmp.product.Westfield_Center_Location__c}" />
</apex:column>
<apex:column id="productName" headerValue="Product Name" style="font-weight: bold; white-space: nowrap;">
<!-- MINI PAGE LAYOUT -->
<apex:outputText rendered="{!tmp.product.Id != null}">
<a target="_blank" id="{!tmp.product.Id}" onmouseover="LookupHoverDetail.getHover('{!tmp.product.Id}', '/{!tmp.product.Id}/m?retURL={!tmp.product.Id}&isAjaxRequest=1').show();" onmouseout="LookupHoverDetail.getHover('{!tmp.product.Id}').hide();" onfocus="LookupHoverDetail.getHover('{!tmp.product.Id}', '/{!tmp.product.Id}/m?retURL={!tmp.product.Id}&isAjaxRequest=1').show();" onblur="LookupHoverDetail.getHover('{!tmp.product.Id}').hide();" href="/{!tmp.product.Id}">{!tmp.product.Name}</a>
</apex:outputText>
</apex:column>
<apex:column id="startDate" headerValue="Start Date" styleClass="{!IF(tmp.index == 0, 'markWhite', '')}" style="white-space: nowrap;">
<apex:outputField value="{!tmp.reservation.Start_Date__c}" style="width: 80px;" />
</apex:column>
<apex:column id="endDate" headerValue="End Date" styleClass="{!IF(tmp.index == 0, 'markWhite', '')}" style="white-space: nowrap;">
<apex:outputField value="{!tmp.reservation.End_Date__c}" style="width: 80px;" />
</apex:column>
<apex:column id="resStatus" headerValue="Reservation Status" style="white-space: nowrap;">
<apex:outputField value="{!tmp.reservation.Reservation_Status__c}" />
</apex:column>
<apex:column id="exclusive" headerValue="Exclusive" style="text-align: center; white-space: nowrap;" headerClass="alignCenter">
<apex:outputField value="{!tmp.reservation.Exclusive__c}" rendered="{!tmp.index != 0}" />
</apex:column>
<apex:column id="medStatus" headerValue="Media Status" style="white-space: nowrap;">
<apex:outputField value="{!tmp.reservation.Media_Status__c}" />
</apex:column>
<apex:column id="discount" headerValue="Discount %" style="text-align: center;" headerClass="alignCenter">
<apex:outputField value="{!tmp.reservation.Discounted_Amount__c}" style="white-space: nowrap;" />
</apex:column>
</apex:pageBlockTable>
What you need to do next is - create another temporary table with the desired columns like this :
<apex:pageBlockTable id="tableFixed" headerClass="headerRow" first="" value="{!tempReservations}" var="tmp" columns="3" style="position: absolute; border: none; display: none;">
<apex:column id="no" headerValue="#" headerClass="alignCenter" style="height: 25px; white-space: nowrap;">
<apex:outputText rendered="{!tmp.index != 0}" value="{!tmp.index}." style="height: 25px;" />
</apex:column>
<apex:column id="centreName" headerValue="Centre Name" style="font-weight: bold; white-space: nowrap; height: 25px;">
<apex:outputField value="{!tmp.product.Westfield_Center_Location__c}" />
</apex:column>
<apex:column id="productName" headerValue="Product Name" style="font-weight: bold; border-right: 2px solid black; white-space: nowrap;">
<!-- MINI PAGE LAYOUT -->
<apex:outputText rendered="{!tmp.product.Id != null}">
<a target="_blank" id="{!tmp.product.Id}" onmouseover="LookupHoverDetail.getHover('{!tmp.product.Id}', '/{!tmp.product.Id}/m?retURL={!tmp.product.Id}&isAjaxRequest=1').show();" onmouseout="LookupHoverDetail.getHover('{!tmp.product.Id}').hide();" onfocus="LookupHoverDetail.getHover('{!tmp.product.Id}', '/{!tmp.product.Id}/m?retURL={!tmp.product.Id}&isAjaxRequest=1').show();" onblur="LookupHoverDetail.getHover('{!tmp.product.Id}').hide();" href="/{!tmp.product.Id}">{!tmp.product.Name}</a>
</apex:outputText>
</apex:column>
</apex:pageBlockTable>
For positioning purposes add the following javascript code just after your tables. As per the comments there are some cross-browser issues which I couldn't figure out back in the day, so play around with the pixels until you get the right positioning.
<script type="text/javascript">
function getAbsY(elt) { return (elt.y) ? elt.y : getAbsPos(elt,"Top"); }
function getAbsPos(elt,which)
{
iPos = 0;
while (elt != null)
{
iPos += elt["offset" + which];
elt = elt.offsetParent;
}
return iPos;
}
var fixedTable = document.getElementById('{!$Component.tableFixed}');
var actualTable = document.getElementById('{!$Component.table}');
var fixedTableWidth = fixedTable.style.width = actualTable.rows[0].cells[0].clientWidth + actualTable.rows[0].cells[1].clientWidth + actualTable.rows[0].cells[2].clientWidth + 'px';
fixedTable.style.minWidth = fixedTable.clientWidth + 'px';
fixedTable.style.borderRight = '2px solid black';
// TODO this cries to be refactored
if('MozBoxSizing' in document.documentElement.style)
{
// firefox adds extra 30px from somewhere...
fixedTable.style.top = (getAbsY(actualTable) - document.getElementById('AppBodyHeader').clientHeight) + 'px';
}
else if(!!(window.opera && window.opera.version))
{
// opera adds only 3px extra, again not sure where from (SF maybe has some issues)
fixedTable.style.top = (getAbsY(actualTable) - document.getElementById('AppBodyHeader').clientHeight + 27) + 'px';
// opera doesn't care about height, even if you explicitly specify, it ignores it. Maybe SF has some weird overrides, no time for that now
var fixedTableCells = fixedTable.getElementsByTagName('td');
for(var i = 0; i < fixedTableCells.length; i++)
{
fixedTableCells[i].style.height = '26px';
}
}
else
{
// safari, chrome
fixedTable.style.top = (getAbsY(actualTable) - document.getElementById('AppBodyHeader').clientHeight + 30) + 'px';
// chrome and safari are adding 1 extra pixel for the cells height, not sure why
var fixedTableCells = fixedTable.getElementsByTagName('td');
for(var i = 0; i < fixedTableCells.length; i++)
{
fixedTableCells[i].style.height = '26px';
}
}
</script>
And finally, you want to attach onscoll
event on the window so that every time the user scrolls, the code executes and determines whether to show or hide the fixed table. Add this code just before your form
or page
tag.
<script type="text/javascript">
// Disable calendar auto popup on page load
function setFocusOnLoad() {}
var startOffset = 0;
window.onscroll = fixTable;
function fixTable(event)
{
var actualTableOffset = GetRealOffset('{!$Component.table}');
var pageOffset = getScrollLeft();
var tablePosition = fixedTable.style.position;
if(actualTableOffset > 0 && pageOffset > actualTableOffset)
{
startOffset = actualTableOffset;
fixedTable.style.display = 'block';
fixedTable.style.left = (pageOffset - 10) + 'px'; // -10 for the padding/margin
}
else if(pageOffset < startOffset || actualTableOffset == 0)
{
fixedTable.style.display = 'none';
}
}
function GetRealOffset(id)
{
var elem = document.getElementById(id);
var leftOffset = elem.offsetLeft;
var parent = elem.offsetParent;
if(parent != null)
{
while(parent != document.body)
{
leftOffset += parent.offsetLeft;
parent = parent.offsetParent;
}
}
return leftOffset;
}
function getScrollLeft()
{
if(typeof pageXOffset != 'undefined')
{
//most browsers
return pageXOffset;
}
else
{
var B = document.body; //IE 'quirks'
var D = document.documentElement; //IE with doctype
D = (D.clientWidth) ? D : B;
return D.scrollLeft;
}
}
</script>
Hope this helps and you should be able to work it out. This is the easiest concept I've come up with so far in regards to fixed tables for scrolling. Comments and suggestions for a better solution are welcomed