Skip to main content

Freeze panes in Interactive Grid : Driven by a page item

Oracle APEX Tutorial

Freeze Panes in Interactive Grid
: Driven by a Page Item

Lock N columns in place with a select list : no plugins, no hacks. Pure JavaScript, a Dynamic Action, and clean CSS that survives sort, search, and pagination.

Create page item
Add page JavaScript
Dynamic Action
Optional CSS
1

Create the P1_FREEZE_COLS select list

In Page Designer, add a Select List page item. Configure it as shown below. Also set the Interactive Grid region's Static ID to emp_grid via Properties → Advanced → Static ID.

NameP1_FREEZE_COLS
List of ValuesStatic Values : Display/Return 0 through 5
Default Value0
TemplateOptional / Floating Label
Region Static IDemp_grid
2

Page JavaScript : Execute when Page Loads

Paste this into Page → JavaScript → Execute when Page Loads. It handles the initial render, post-sort, pagination, and every apexafterrefresh event automatically.

JavaScript
(function () {
  "use strict";

  var REGION_ID  = "emp_grid";       
  var ITEM_NAME  = "P1_FREEZE_COLS";  

  function clearFreeze() {
    var $tbl = apex.region(REGION_ID).widget().find(".a-GV-table");
    $tbl.find("thead tr th, tbody tr td").css({
      position    : "", left        : "", zIndex      : "",
      background  : "", boxShadow   : "", borderRight : ""
    }).removeClass("ig-frozen-col ig-freeze-edge");
  }

  function applyFreeze(numCols) {
    clearFreeze();
    numCols = parseInt(numCols, 10);
    if (!numCols || numCols < 1) return;

    var $tbl = apex.region(REGION_ID).widget().find(".a-GV-table");
    if (!$tbl.length) return;

    var colWidths = [];
    $tbl.find("thead tr:first-child th").each(function (i) {
      colWidths[i] = $(this).outerWidth() || 120;
    });

    $tbl.find("thead tr, tbody tr").each(function () {
      var $row    = $(this);
      var inHead  = !!$row.closest("thead").length;
      var cumLeft = 0;

      $row.children("th, td").each(function (colIdx) {
        if (colIdx >= numCols) return false;
        var $cell  = $(this);
        var isEdge = (colIdx === numCols - 1);

        $cell.css({
          position   : "sticky",
          left       : cumLeft + "px",
          zIndex     : inHead ? 31 : 20,
          background : inHead ? "#1a5f9e" : "#e8f0fa",
          boxShadow  : isEdge ? "2px 0 0 0 #0572ce" : ""
        });
        cumLeft += colWidths[colIdx];
      });
    });

    apex.region(REGION_ID).widget()
        .find(".a-GV-w-scrollX")
        .css("overflow-x", "auto");
  }

  function applyFromItem() {
    var val = $v(ITEM_NAME) || "0";
    applyFreeze(val);
  }

  apex.jQuery(document)
    .on("gridviewcreate apexafterrefresh", "#" + REGION_ID,
        function () { setTimeout(applyFromItem, 200); });

  setTimeout(applyFromItem, 800);

  window.igApplyFreeze = applyFromItem;
}());
3

Dynamic Action on P1_FREEZE_COLS

Right-click P1_FREEZE_COLS in Page Designer → Create Dynamic Action. Configure the event and add a True Action with the one-liner below. It calls the function already defined in Step 2 : no duplication needed.

EventChange
ItemP1_FREEZE_COLS
True ActionExecute JavaScript Code
JavaScript
igApplyFreeze();
4

Optional : Inline CSS for zebra striping

Add this to Page → CSS → Inline. Keeps the sticky header above the scroll layer and preserves the alternate-row shading on frozen cells.

CSS

#emp_grid .a-GV-w-scrollX {
  overflow-x: auto !important;
}


#emp_grid .a-GV-thead {
  position: sticky;
  top: 0;
  z-index: 30;
}

#emp_grid .a-GV-table tbody tr:nth-child(even) td[style*="sticky"] {
  background: #d4e4f7 !important;
}

How it works

When the user changes P1_FREEZE_COLS, $v("P1_FREEZE_COLS") reads the selected value in real time.
Each column's actual pixel width is measured from the live DOM on every call : zero hardcoding needed.
position: sticky is applied with the correct cumulative left offset to every <th> and <td> inside the frozen range.
A teal box-shadow line marks the freeze boundary on the rightmost frozen column : just like Excel's freeze pane indicator.
Freeze re-applies automatically after every sort, search, and pagination event via the apexafterrefresh hook : no manual trigger needed.

Quick Checklist

Add P1_FREEZE_COLS select list with static values 0–5 and default value 0
Set the Interactive Grid region Static ID to emp_grid via Properties → Advanced → Static ID
Paste the Step 2 JavaScript into Page → JavaScript → Execute when Page Loads
Create Dynamic Action on Change event → Execute JavaScript → igApplyFreeze();

Comments

Popular posts from this blog

Screen Recorder in Oracle APEX (Single Page)

Oracle APEX Tutorial Screen Recorder in Oracle APEX : Single Page Build a fully functional browser-based screen recorder inside Oracle APEX using just one Static Content region and native JavaScript. No plugins, no external libraries, no server uploads required. ✍️ Why I Built This I was working on a client project where the support team needed to record screen issues and share them directly from the APEX application, without switching to any external tool. Installing third-party software was not an option on their machines, and every screen recorder extension required IT approval. That is when I thought: the browser already has everything we need. Why not build it right inside APEX? That idea turned into this. &#127916; Start / Stop Recording &#128065; Instant Preview ⬇️ One-click Download &#128266; Audio + Video ...

Excel-Style Keyboard Shortcuts in Interactive Grid

Oracle APEX Tutorial Excel-Style Keyboard Shortcuts in Interactive Grid Add Alt+A , Alt+D , Alt+S and Alt+R to your editable IG : no plugins, just four clean setup steps using APEX's built-in actions registry. Alt + A   Add Row Alt + D   Delete Row Alt + S   Save Alt + R   Refresh Alt + Shift + F1   View All Shortcuts Live Demo GitHub Code Editable IG Static ID Cursor Focus JS Init Code Testing 1 Prerequisites : Make your IG Editable ⚠️ Keyboard shortcuts only work on an Editable Interactive Grid . The row-add-row , row-delete , and save actions only exist in the IG's action registry when the grid is in editable mode. If you skip this, actions.lookup() will return null and throw an error. In your Interactive Grid region's attributes, set Edit → Enabled to...