Skip to main content

IG Drag Fill Series

Excel Drag Fill in Oracle APEX Interactive Grid
Oracle APEX · Interactive Grid

Excel-Like
Drag & Fill
in APEX IG

"I spent way too long trying to get this working…
and then one weird jQuery trick changed everything."

Oracle APEX Interactive Grid jQuery JavaScript PL/SQL CSS
scroll

The Backstory

If you've used Excel for even ten minutes, you already know that little green handle at the corner of a selected cell. You drag it down, and boom, values fill across every row automatically. It's one of those features users love so much they don't even think about it. They just expect it everywhere.

So when a client asked me, "Hey, can we have that same fill-down thing in our APEX grid?". I said, "Yeah, shouldn't be a problem."

Reader: it was a problem.


What I Tried First (spoiler: it failed)
๐Ÿ˜…
I tried HTML5 draggable="true": it was a disaster
My first instinct was the obvious one: slap draggable="true" on every editable cell, use the native dragstart / drop events, and wire them to the model. Spent about 45 minutes on this. Works great in a static table. Falls apart completely in an Interactive Grid because APEX re-renders rows constantly. Every time the model updates, all your event listeners vanish. Back to square one.
๐Ÿคฆ
I tried hardcoding column names: terrible idea
My second attempt hardcoded the column name in the drag handler. So model.setValue(record, "CITY", value). It worked! For exactly one column. The moment I needed it to work across any column dynamically, the whole approach crumbled. Also, the day I rename a column in the DB, everything breaks silently. Not great.
⚠️
One issue I faced: mousedown breaking cell editing
Once I switched to mousedown for capturing the drag start, clicking any cell to edit it started misbehaving. The grid's own focus management was colliding with my handler. The fix? One tiny line: event.preventDefault(). I cannot tell you how long I stared at this before figuring that out. Maybe 30 minutes. Maybe more. We don't talk about it.
๐Ÿ”
Getting the column name dynamically was non-obvious
APEX doesn't expose the column name directly on a cell click. I had to get the cell's index in its row, then look that up in the IG model's modelColumns config object using Object.keys(). Feels like a hack, but it's actually the supported pattern, and it works consistently.

Approaches Compared
ApproachWorks?Why / Why Not
HTML5 draggable✗ NoEvents die on APEX grid re-render
Hardcoded column names✗ FragileBreaks on rename, not scalable
mousemove on cells (direct)✗ NoRe-rendered cells lose listeners
Delegated mousedown + mousemove✓ YesSurvives re-renders, dynamic columns

The Approach That Finally Worked
Finally, this approach worked: delegated mouse events and the APEX model API
Three mouse events chained together, all delegated from $(document) so they survive any re-render: mousedown captures the value and column name when you press down on a cell, mousemove on rows fills the captured value into each row you hover over while holding, and mouseup resets everything cleanly. The APEX IG model does all the heavy lifting.
1
Set Up the IG Source Query
Clean SELECT from your EMPLOYEES table. The Region Static ID must be my_ig. The JS references it by that exact ID.
2
Add checkname CSS Class to Columns
In the APEX column settings, set the CSS class to checkname for every column you want drag-fill enabled on. This is the hook the JavaScript uses.
3
Paste the JavaScript in Execute on Page Load
The three-event delegation handles everything. It reads from and writes to the IG model directly, so APEX tracks changes properly for save.
4
Add the CSS for the Handle Indicator
A pure-CSS ::after pseudo-element on .checkname adds the red ^ drag handle visual, no extra HTML needed.
5
Wire the PL/SQL DML Process
Standard IG process using APEX$ROW_STATUS. Make sure every bind variable exactly matches your column names. Case matters.

IG Source Query

Nothing fancy here, just a straightforward SELECT. The magic is entirely in the JS layer.

IG Source Code
SELECT ROW_ID,
       EMPLOYEE_NAME,
       MANUAL_VALUE,
       PHONE_NUMBER,
       CITY,
       PIN_CODE
FROM   EMPLOYEES;

JavaScript: Execute When Page Loads

Why delegated events? APEX's IG re-renders table rows on every model change. If you attach events directly to .checkname elements, they disappear the next time the grid updates. Delegating from $(document) means your listeners always catch events no matter how many times the DOM rebuilds.

Execute When Page Loads
$(document).ready(function () {

    let draggedValue  = null;
    let draggedColumn = null;

$(document).on("mousedown", ".checkname", function (event) {

        let $cell   = $(this);
        let $row    = $cell.closest("tr");
        let ig$     = apex.region("my_ig").widget();
        let model   = ig$.interactiveGrid("getViews", "grid").model;
        let record  = model.getRecord($row[0].dataset.id);

let columnIdx  = $cell.index();
        let view       = ig$.interactiveGrid("getViews", "grid");
        let config     = view.modelColumns;
        let columnName = Object.keys(config)[columnIdx];

        if (columnName) {
            draggedValue  = model.getValue(record, columnName);
            draggedColumn = columnName;
        }

event.preventDefault();
    });

$(document).on("mousemove", "tr", function () {
        if (draggedValue !== null && draggedColumn !== null) {
            let $row   = $(this);
            let ig$    = apex.region("my_ig").widget();
            let model  = ig$.interactiveGrid("getViews", "grid").model;
            let record = model.getRecord($row[0].dataset.id);

            model.setValue(record, draggedColumn, draggedValue);
        }
    });

$(document).on("mouseup", function () {
        draggedValue  = null;
        draggedColumn = null;
    });

});

CSS: The Handle and Visual Cues

The ::after on .checkname adds the red ^ handle, a visual signal that the column supports drag-fill. Took me a few tries to get the positioning right so it doesn't overlap the cell text.

CSS
.manual-value-container {
    position:      relative;
    display:       flex;
    align-items:   center;
    width:         100%;
}

.manual-value-input {
    flex-grow:     1;
    padding-right: 30px;
    border:        1px solid #ccc;
    padding:       4px;
    border-radius: 4px;
    width:         100%;
}

.drag-handle {
    cursor:        grab;
    color:         #007bff;
    margin-right:  5px;
}

.checkname {
    position:      relative;
    padding-right: 25px;
}

.checkname::after {
    content:           "^";
    font-size:         16px;
    font-weight:       bold;
    color:             #db3338;
    position:          absolute;
    top:               50%;
    right:             5px;
    transform:         translateY(-50%);
    background:        white;
    border-radius:     50%;
    width:             18px;
    height:            18px;
    display:           flex;
    align-items:       center;
    justify-content:   center;
    box-shadow:        0 0 3px rgba(0,0,0,0.3);
    cursor:            pointer;
}

button#B24398529473943637001 {
    background-color: cadetblue;
}

PL/SQL: DML Process

Standard IG process, nothing surprising. The one thing I always double-check: bind variable names must match your column names exactly. APEX is case-insensitive here but it's a good habit to keep them consistent.

Process Code
BEGIN
    CASE :APEX$ROW_STATUS

    WHEN 'C' THEN  
        INSERT INTO EMPLOYEES (
            ROW_ID, EMPLOYEE_NAME, MANUAL_VALUE,
            PHONE_NUMBER, CITY, PIN_CODE
        )
        VALUES (
            :ROW_ID, :EMPLOYEE_NAME, :MANUAL_VALUE,
            :PHONE_NUMBER, :CITY, :PIN_CODE
        );

    WHEN 'U' THEN  
        UPDATE EMPLOYEES
           SET EMPLOYEE_NAME = :EMPLOYEE_NAME,
               MANUAL_VALUE  = :MANUAL_VALUE,
               PHONE_NUMBER  = :PHONE_NUMBER,
               CITY          = :CITY,
               PIN_CODE      = :PIN_CODE
         WHERE ROW_ID = :ROW_ID;

    WHEN 'D' THEN  
        DELETE FROM EMPLOYEES
         WHERE ROW_ID = :ROW_ID;

    END CASE;
END;

๐ŸŽ‰ Finally working and the client loves it

Once all the pieces clicked into place, the whole thing works exactly like Excel's fill handle. The APEX model handles dirty tracking automatically, so hitting Save just works. Users have been using it daily without a single bug report. That's a win.


Things I'd Tell Past Me
๐Ÿ’ก
Static ID must be exact: my_ig
apex.region("my_ig"): if your region's Static ID doesn't match this string exactly, you'll get a silent JS error and nothing will work. Check the region's Static ID property in Page Designer first.
๐Ÿ’ก
Add checkname class per-column in Column Attributes
Go to each column's settings in Page Designer → Column Attributes → CSS Classes. Only columns with this class will get drag-fill behavior. Leave header columns and read-only ones without it.
๐Ÿš€
Next idea: series fill (1, 2, 3... auto-increment)
True Excel behavior also increments numbers as you drag, not just copy the same value. That's absolutely doable: track the starting value and the row index offset, then add the delta per row. Working on it now, watch the GitHub repo for updates.

Try It

The live demo is up. Pull the source on GitHub and make it yours.

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. 🎬 Start / Stop Recording 👁 Instant Preview ⬇️ One-click Download 🔊 Audio + Video ...

Sticky Notes Widget Inside Oracle APEX

Oracle APEX Project Building a Sticky Notes Widget in Oracle APEX How I built a fully draggable, color-coded, per-user sticky notes board using jQuery UI, APEX Ajax callbacks, and a bit of patience. Live Demo GitHub Repo Oracle APEX jQuery UI PL/SQL Ajax Callbacks JavaScript CSS Introduction Why I Built This I have been building internal tools on Oracle APEX for a while now, and one thing I always felt was missing was a place where users could quickly jot down thoughts without leaving the page. Think of it like a personal scratchpad that lives right inside the app. I had seen sticky note UIs in some Google products and I thought, how hard can this be in APEX? It turned out to be more interesting than I expected. There were a few wrong tu...