Scrollbare APEX-Berichte: Freeze Panes

Insbesondere bei längeren Berichten wünschen sich viele Endanwender die aus Microsoft Excel bekannte Freeze Panes-Funktion. Auf einen APEX-Bericht übertragen bedeutet dies, dass eine Scrollleiste hinzugefügt wird, mit der man sich durch den Bericht durchscrollen kann (Abbildung 1).

"Freeze Panes" mit einem APEX-Bericht
Abbildung 1:"Freeze Panes" mit einem APEX-Bericht

Auf den ersten Blick scheint das recht einfach zu sein; HTML und CSS bieten Attribute an, mit denen die Höhe einer Region und die Anwesenheit von Scrollleisten festgelegt werden kann. Der erste Ansatz wäre also die Navigation zu den Regionseigenschaften und dort zum Regions-Header und -Footer. Tragen Sie wie in Abbildung 2 dargestellt, <div style="height: 450px; overflow: scroll"> in den Regions-Header und </div> in den Regions-Footer ein.

Erster Versuch einer Umsetzung: Nur mit CSS-Eigenschaften
Abbildung 2: Erster Versuch einer Umsetzung: Nur mit CSS-Eigenschaften

Wenn Sie die Scollleiste nun jedoch benutzen, erkennen Sie sofort, dass dies alleine noch nicht ausreicht. Wie Abbildung 3 zeigt, werden die Tabellenüberschriften beim Betätigen der Scrollleiste "mitgescrollt"; CSS-Eigenschaften alleine reichen also nicht aus. Entfernen Sie daher die gemachten Eintragungen wieder aus dem Regions-Header und -Footer Ihres Berichts.

Reine CSS-Eigenschaften reichen nicht aus
Abbildung 3: Reine CSS-Eigenschaften reichen nicht aus

Damit die Inhalte von den Überschriften getrennt gescrollt werden können, muss die Tabelle gewissermaßen gesplittet werden; es müssen also zwei HTML-Tabellen entstehen. Die eine enthält die Überschriften, die andere die Inhalte. Die Herausforderung dabei ist die exakte Ausrichtung der Spaltenbreiten beider Berichte - die Spalten beider HTML-Tabellen müssen exakt aneinander ausgerichtet werden.

JavaScript-Lösung für "scrollbare" Berichte

Der in diesem Tipp vorgestellte JavaScript-Code ist in der Lage, die beschriebenen Schritte für einen APEX-Bericht durchzuführen - er funktioniert allerdings nur mit "klassischen" Berichten. Damit die Scrollleisten auch beim Ändern der Sortierung oder beim Blättern im Bericht erhalten bleiben, schalten Sie außerdem die Partial Page Refresh Eigenschaft ab (Abbildung 4).

Partial Page-Eigenschaft abschalten
Abbildung 4: Partial Page-Eigenschaft abschalten

Binden Sie dann den folgenden JavaScript-Code in Ihre APEX-Anwendung ein; nehmen Sie ihn also ins Seitentemplate auf oder tragen Sie ihn bei den Seitenattributen in den Header Text ein.

<script type="text/javascript">
  var g_freezeRegionIds = new Array();
  var g_freezeRegionHeights = new Array();
  var g_freezeRegions = 0;

  /*
   * Diese Funktion erzeugt direkt über der von APEX
   * generierten Berichtstabelle eine neue mit den 
   * Überschriften
   */
  function createHeadingTable(p_tableNode, p_headingRowNode) {
    var l_te = p_tableNode.cloneNode(false);
 //    l_te.setAttribute("id", l_te.getAttribute("id")+ "FREEZEPANE_HEADINGS");
    var l_tb = l_te.appendChild(document.createElement("thead"));
    (p_tableNode.parentNode).insertBefore(l_te, p_tableNode);
    l_thr = p_headingRowNode.cloneNode(true);
    l_tb.appendChild(l_thr);
    return l_thr;
  }

  /*
   * Die Funktion "registerScroll" muss für jede Region
   * aufgerufen werden, für die das Scrolling aktiviert
   * werden soll.
   */
  function registerScroll(p_regionId, p_newHeight) {
    g_freezeRegionIds[g_freezeRegions] = p_regionId;
    g_freezeRegionHeights[g_freezeRegions] = p_newHeight;
    g_freezeRegions++;
  }

  /*
   * die Funktion "do_freezePanes()" muss im onLoad-Attribut
   * des Body-Tags stehen 
   */
  function do_freezePanes() {
    for (var i=0;i<g_freezeRegions;i++) {
      freezePanes(g_freezeRegionIds[i], g_freezeRegionHeights[i]);
    }
  }

  /*
   * die Funktion "freezePanes()" wandelt einen normalen
   * APEX-Bericht in einen Scroll-Fähigen um.
   */
  function freezePanes(p_regionid, p_newHeight) {
   var l_region = document.getElementById("report_"+p_regionid);
   var l_heading = l_region.getElementsByTagName("th")[0];
   var l_table = l_heading.parentNode.parentNode.parentNode;
   splitTable(l_table, p_newHeight); 
  }

  function splitTable(p_tableNode, p_newHeight) {
   var l_trs = p_tableNode.getElementsByTagName("tr");
   var l_colWidth = 0;
   if (l_trs) {
     if (l_trs.length > 1) {
       var l_headrow = l_trs[0];
       var l_datarow = l_trs[1];
       var l_hcols = l_headrow.getElementsByTagName("th");
       var l_dcols = l_datarow.getElementsByTagName("td");
    
       var l_newheadrow = createHeadingTable(p_tableNode, l_headrow);
       var l_newheadcols = l_newheadrow.getElementsByTagName("th");

       if (l_hcols) {
         for (var i=0;i<l_hcols.length;i++) {
           l_colWidth = l_hcols[i].offsetWidth + "px";
           l_newheadcols[i].style.width    = l_colWidth;
           l_newheadcols[i].style.minWidth = l_colWidth;
           l_newheadcols[i].style.maxWidth = l_colWidth;
           l_dcols[i].style.width          = l_colWidth;
           l_dcols[i].style.minWidth       = l_colWidth;
           l_dcols[i].style.maxWidth       = l_colWidth;

         }
       }
       l_headrow.style.display="none";
       var l_div = document.createElement("div");
       (p_tableNode.parentNode).insertBefore(l_div, p_tableNode);
       l_div.appendChild(p_tableNode);

       l_div.style.height = p_newHeight;
       l_div.style.overflow = "scroll"
     }
   }
  }
</script>

Navigieren Sie anschließend zum Regions-Footer Ihrer Berichtsregion. Aktivieren Sie die Scrollleisten für den Bericht darin wie in Abbildung 5 dargestellt.

<script type="text/javascript">
  registerScroll(
    '#REGION_ID#',    // Regions-ID des Berichts
    '300px'           // Neue Höhe als CSS-Eigenschaft (hier: 300 Pixel)
  );
</script>
Scrolling für einen Bericht aktivieren: im Regions-Footer
Abbildung 5: Scrolling für einen Bericht aktivieren: im Regions-Footer

Navigieren Sie zum Abschluß nochmals zu den Seitenattributen und dort zum HTML Body Attribut. Tragen Sie dort onLoad="do_freezePanes();" ein (Abbildung 6).

Berichts-Scrolling global einschalten
Abbildung 6: Berichts-Scrolling global einschalten

Fertig. Starten Sie nun die Berichtsseite - sie sollte dann wie in Abbildung 7 aussehen.

Bericht mit Scrollleiste - diesmal ein anderes Template
Abbildung 7: Bericht mit Scrollleiste - diesmal ein anderes Template

Der JavaScript-Code sollte mit den Berichts-Templates aus allen APEX-Themes zusammenarbeiten, die nicht zentriert sind. Bei zentrierten Templates (bspw. Theme Nr 7: Centered) kann es sein, dass die Tabellenspalten nicht exakt aneinander ausgerichtet sind - stellen Sie das Layout dann auf linksbündig um. Ebenfalls nicht unterstützt sind die "default"-Templates, also die Berichts-Templates, die nicht zu einem Theme gehören. Abbildung 8 zeigt den Unterschied: Grün markiert sind die zu einem Theme gehörenden Templates - diese funktionieren mit dem JavaScript-Code.

Unterstützte und nicht unterstützte Report-Templates
Abbildung 8: Unterstützte und nicht unterstützte Report-Templates

Zurück zur Community-Seite