Preventing "False Positive" Selection Change Events in Interactive Grids
Software Engineer at Optimizer
If you use Interactive Grids in Oracle APEX, you’ve probably hooked into the Selection Change dynamic action to react when the user changes selected rows. The annoying part: the event can fire twice when the page loads, even though the user hasn’t touched the grid.
This post shows a small JavaScript helper that acts like a selection-aware debounce, so your dynamic action only runs when the selection has actually changed.
TL;DR
On some pages,
Selection Change [Interactive Grid]fires twice on page load, before the user makes any change.We create a selection fingerprint from the currently selected row IDs and store it on the region element.
If the fingerprint hasn’t changed since the last event, we return
falsefrom a Client-side Condition, so your dynamic action does not run when there is no actual change.
Scroll down for the full code and step‑by‑step setup.
The Problem
Selection Change [Interactive Grid] is useful, but noisy:
- It can fire twice on first load, even though the user hasn’t changed the selection.
That means:
Unnecessary server calls or refreshes
Initialization logic running once “too early”
Confusing logs and side effects when nothing appears to have changed
The Idea: Selection “Fingerprint”
The key idea is to treat the current selection as a small fingerprint:
Read the currently selected records from the Interactive Grid.
Convert each record into its record ID.
Sort those IDs and join them into a single string, for example:
"1050""1050:2033"""(for “no selection”)
Compare this fingerprint with the last stored fingerprint.
Only if they differ do we consider it a real selection change.
We store this fingerprint in the DOM (using jQuery’s .data() attribute on the region element), so it persists across multiple event firings during the same page lifecycle.
The JavaScript Helper
The helper is implemented once in a shared workspace JavaScript file and then reused by any Interactive Grid that needs it.
// File: workspace_utils.js
var Demo = Demo || {};
Demo.checkSelection = function(daContext) {
try {
// Automatically find the Region from the context
const regionElement = daContext.triggeringElement;
// Safety check: ensure we actually have an element
if (!regionElement) return false;
const regionId = regionElement.id;
const region = apex.region(regionId);
if (!region) {
console.warn("Demo: Region not found", regionId);
return false;
}
// Get Grid and Model
if (!region.widget()) return false;
const grid = region.widget().interactiveGrid("getViews", "grid");
if (!grid || !grid.model) return false;
const model = grid.model;
const records = grid.getSelectedRecords() || [];
// Create "Fingerprint"
const currentFingerprint = records.map(function(r) {
return model.getRecordId(r);
}).sort().join(":");
// Get last state
const $el = apex.jQuery(regionElement);
const lastFingerprint = $el.data("selection-fingerprint");
// No previous fingerprint yet -> IGNORE
if (lastFingerprint === undefined) {
$el.data("selection-fingerprint", currentFingerprint);
return false;
}
// Fingerprint matches last event -> IGNORE
if (currentFingerprint === lastFingerprint) {
return false;
}
// Valid Change -> UPDATE & FIRE
$el.data("selection-fingerprint", currentFingerprint);
return true;
} catch (e) {
console.error("Demo: Error in checkSelection", e);
return false;
}
};
How to Use This in Your APEX App
1. Add the helper to a workspace JavaScript file
Create a Workspace Static File called
workspace_utils.js.Paste the
Demo.checkSelectionfunction from above into that file.
2. Reference the file in User Interface Attributes
Go to Shared Components → User Interface Attributes → JavaScript.
In File URLs, add:
#WORKSPACE_FILES#workspace_utils#MIN#.js
This way, the helper is automatically loaded and can be used on all pages.
3. Create or Adjust the Dynamic Action
Create a Dynamic Action on your Interactive Grid region:
Event:
Selection Change [Interactive Grid]Selection Type:
RegionRegion: the Interactive Grid region name
Add your existing True Actions (Execute PL/SQL, Refresh Region, etc.) or create new ones.
4. Use the function as a Client-side Condition
In your Dynamic Action, set Client-side Condition → JavaScript Expression to:
Demo.checkSelection(this)
The helper inspects the Interactive Grid behind the dynamic action, compares the current selection fingerprint with the last stored one, and returns true only when there is a real change in the selected rows.
Why This Works
First events on page load:
There is no stored fingerprint yet (lastFingerprint === undefined).
The script stores the current selection but returnsfalse, so the action does not run.Events without a real change:
The selection is identical to the last stored state, socurrentFingerprint === lastFingerprint.
The function returnsfalse, preventing the action from running when nothing has actually changed.Actual user selection change:
The selected rows are different, so the fingerprint string changes.
The script updates the stored fingerprint and returnstrue, allowing the dynamic action to fire.
This simple comparison lets you acknowledge that Selection Change may fire on load while ensuring your dynamic action only runs when the selection really changes.
Variations and Notes
Multiple grids:
You can reuse this logic for multiple grids simply by usingDemo.checkSelection(this)as the Client-side Condition expression in each Interactive Grid’s Dynamic Action.Performance:
For typical grid sizes, mapping record IDs and joining them is very cheap. Even for larger grids, this runs only when theSelection Changeevent fires.Empty selection:
No selection becomes an empty string"". The comparison still works correctly and suppresses redundant events.
Closing Thoughts
The Selection Change event of an Oracle APEX Interactive Grid can be noisy, especially on page load. By creating and comparing a lightweight selection fingerprint, you can make sure your dynamic actions fire only when the user really changes the selection.
Once you drop this helper into your app, Interactive Grid behavior becomes much more predictable and you avoid unnecessary processing and confusing side effects in your APEX applications.