Reports, interactive grids, cards, charts, maps, calendars, trees, and faceted search all have something in common in Oracle APEX: a region interface. It is the JavaScript object that lets you refresh the region, move focus into it, reach the widget behind it, and listen to what it does.
This guide covers that shared interface and the four functions in the apex.region namespace, including the two that give a plug-in region, or any region you like, an interface of its own. Each method has a tested example with its real output, and two recipes at the end show an auto-refreshing dashboard region and a loading spinner over a region.
Quick Reference
| Member | What it does |
|---|---|
| element, type, widgetName | The region's element, its kind, and the widget that implements it |
| filterRegionId, parentRegionId | The faceted search that filters it, or its master region |
| refresh | Reloads or redraws the region, returning a promise for some types |
| focus | Moves keyboard focus into the region |
| widget, call | Reach the region's widget and call its methods |
| on, off | Attach and remove handlers for the widget's events |
| alternateLoadingIndicator | Where an interactive grid wants a column item's spinner |
| apex.region.isRegion, apex.region.findClosest | Check an ID, or find the region around an element |
| apex.region.create, apex.region.destroy | Add or remove a region interface, as plug-ins do |
How to Run These Examples
The examples ran in Oracle APEX 26.1 on a test application: a products page with a classic report filtered by faceted search, a product catalog in cards, a category tree, a stores interactive grid, a customers interactive report, and a home page with a list of recent orders. The output under each example is exactly what the browser console printed.
To try one, open a page of your own, press F12, paste the code into the Console tab, and replace the region IDs with your own. Every example addresses regions by their HTML DOM ID, which you set under Advanced in the region's properties. Give every region you script a DOM ID of your own; the generated ones change when a region is copied.
Which Regions Have a Region Interface?
Only regions with behavior of their own get an interface. Call apex.region on a static content region and you get null. For the rest, the type property names the kind of region and widgetName names the widget behind it, if any:
| Region | type | widgetName |
|---|---|---|
| Interactive report | InteractiveReport | none |
| Interactive grid | InteractiveGrid | interactiveGrid |
| Classic report | ClassicReport | none |
| Faceted search | Facets | facets |
| Cards | Cards | tableModelView |
| Tree | Tree | treeView |
| Map | SpatialMap | spatialMap |
| Calendar | FullCalendar | fullCalendar |
| Chart | JetChart | ojChart |
| Template component report | TemplateComponent | none |
| Dynamic content | DynamicContent | none |
| Any other region with an interface | generic | none |
The widgetName matters because widget, call, on, and off only work for regions that have one.
Region Interface Properties
| Property | Type | Description |
|---|---|---|
| element | jQuery | The region's outer element. |
| type | string | The region type, from the table above. |
| widgetName | string | The widget that implements the region, or null. |
| filterRegionId | string | For a region filtered by faceted search or smart filters, the DOM ID of the filter region. |
| parentRegionId | string | For the detail region of a master-detail layout, the DOM ID of the master region. |
On the products page, looping over apex.regions shows the faceted search and the report it filters, and the report knows which region filters it:
for (const region of Object.values(apex.regions)) {
console.log(region.element.attr("id"), "- type:", region.type,
"- widgetName:", region.widgetName, "- filterRegionId:", region.filterRegionId);
}Output:
product_search - type: Facets - widgetName: facets - filterRegionId: null products - type: ClassicReport - widgetName: null - filterRegionId: product_search
Refreshing and Focusing a Region
refresh
Fetches new data from the server, or otherwise redraws the region. For many region types it returns a promise that resolves once the refresh is finished. It replaces the older habit of triggering the apexrefresh event yourself, and it fires apexbeforerefresh and apexafterrefresh around the refresh.
region.refresh([pKeepPagination]) → Promise
| Parameter | Type | Description |
|---|---|---|
| pKeepPagination | boolean | Keep the current page of results and scroll position, for regions that support it, such as the interactive report. |
const products = apex.region("products");
products.element.on("apexafterrefresh", () => console.log("apexafterrefresh"));
const result = products.refresh();
console.log("refresh returned:", typeof result.then === "function" ? "a promise" : result);
await result;
console.log("refresh done:", products.element.find("tbody tr").length, "rows");Output:
refresh returned: a promise apexafterrefresh refresh done: 51 rows
The catch is that not every region returns a promise. In 26.1, classic reports, template component reports, cards, calendars, and maps do. Interactive reports, interactive grids, charts, trees, and faceted search return undefined, so await region.refresh() does not actually wait for them. For those, wait for the apexafterrefresh event instead:
const done = new Promise((resolve) => region.element.one("apexafterrefresh", resolve));
region.refresh();
await done;The reverse trap exists too: a template component report returns a promise but, in 26.1, fires neither apexbeforerefresh nor apexafterrefresh, so wait for its promise. Regions that cannot refresh at all simply ignore the call.
The declarative way is a Refresh dynamic action, shown in refreshing a report region with a dynamic action, and a classic use of refresh from code is refreshing parent page regions when a dialog closes.
focus
Moves keyboard focus into the region. Where it lands depends on the region type: in cards, reports, and grids it goes to the current row or card.
region.focus()
apex.region("catalog").focus();
console.log("focus is in:", apex.jQuery(document.activeElement).closest(".a-CardView-item").attr("data-id")
? "the first card" : document.activeElement.tagName);Output:
focus is in: the first card
Use it after an action that leaves focus somewhere unhelpful, such as closing a dialog or applying a filter, so keyboard users land back in the data.
Working with the Widget Behind a Region
widget
Returns the widget element of a region built as a jQuery UI style widget, such as the interactive grid, cards, tree, or calendar, or null for regions without one. On that element you call the widget's methods the jQuery UI way, passing the method name as a string.
region.widget() → jQuery | null
const tree$ = apex.region("categories").widget();
console.log("widget element:", tree$.attr("id"), "- is a treeView:", tree$.hasClass("a-TreeView"));
console.log("top-level nodes:", tree$.treeView("getNodes", tree$.find(".a-TreeView-node--topLevel").first()).length);Output:
widget element: categories_tree - is a treeView: true top-level nodes: 1
call
Calls a method of the region's widget and returns the result. It does the same as region.widget() followed by the widget call, in one step and without needing to know the widget's name.
region.call(pMethod, ...args) → *
const stores = apex.region("stores");
const grid = stores.call("getViews", "grid"); // interactiveGrid getViews method
console.log("records:", grid.model.getTotalRecords());
console.log("current view:", stores.call("getCurrentViewId"));Output:
records: 13 current view: grid
getViews and getCurrentViewId are interactive grid methods; call works the same way for any widget method. To act on the rows a user picked, see how to get selected rows from an interactive grid.
on
Attaches a handler to an event of the region's widget.
region.on(events, ...args)
const stores = apex.region("stores");
stores.on("interactivegridselectionchange", function (event, data) {
console.log("selected stores:", data.selectedRecords.length);
});
const grid = stores.call("getViews", "grid");
stores.call("setSelectedRecords", [grid.model.recordAt(0), grid.model.recordAt(1)], true);Output:
selected stores: 2
Always use the full event name, as the example does. The documentation says you may drop the widget prefix and write "selectionchange" instead of "interactivegridselectionchange". In 26.1, though, the short name also catches the browser's own selectionchange events from text inputs inside the grid, which arrive with no data argument and break a handler that reads data.selectedRecords.
off
Removes a handler that on attached. Pass the same event name and the same function reference, which means the handler must be a named function or stored in a variable, not written inline.
region.off(events, ...args)
const stores = apex.region("stores");
const handler = (event, data) => console.log("selected stores:", data.selectedRecords.length);
stores.on("interactivegridselectionchange", handler);
const grid = stores.call("getViews", "grid");
stores.call("setSelectedRecords", [grid.model.recordAt(1)], true);
await new Promise((resolve) => setTimeout(resolve, 300));
stores.off("interactivegridselectionchange", handler);
stores.call("setSelectedRecords", [grid.model.recordAt(2), grid.model.recordAt(3)], true); // not logged
console.log("handler removed");Output:
selected stores: 1 handler removed
The grid selects its first row when the page loads, so the example starts with the second row: selecting a row that is already selected is not a change and fires nothing. The second selection happens after off, so it is not logged.
alternateLoadingIndicator
Only regions with column items, which in practice means the interactive grid, have this method. When a column item refreshes while it is not visible on its own, the region hands back a better place for the loading spinner, such as the grid cell being edited. Check that the method exists before calling it, since other regions do not have it.
region.alternateLoadingIndicator(pElement, pLoadingIndicator$) → jQuery
const stores = apex.region("stores");
const grid = stores.call("getViews", "grid");
const city = grid.getColumns().find((column) => column.property === "CITY");
// edit the City cell of the first store: its column item is now in that cell
stores.call("getActions").set("edit", true);
grid.view$.grid("gotoCell", grid.model.getRecordId(grid.model.recordAt(0)), "CITY");
await new Promise((resolve) => setTimeout(resolve, 500));
const indicator$ = apex.jQuery('<span class="u-Processing u-Processing--inline"></span>');
const placed$ = stores.alternateLoadingIndicator($x(city.elementId), indicator$);
console.log("indicator placed:", placed$.length, "- in a grid cell:", placed$.closest("td").length === 1);Output:
indicator placed: 1 - in a grid cell: true
You will rarely call this yourself. It matters mostly to authors of item plug-ins that refresh, such as a custom cascading list, so their spinner shows up in the right place when the item is used as a grid column.
The apex.region Namespace Functions
apex.region.isRegion
Returns true when an element with the given ID exists and has a region interface.
apex.region.isRegion(pRegionId) → boolean
console.log(apex.region.isRegion("customers"));
console.log(apex.region.isRegion("no_such_region"));
console.log(apex.region.isRegion("P2_NO_SUCH_ITEM"));Output:
true false false
apex.region.findClosest
Returns the interface of the region that contains a given element, or null when the element is not inside one. It is useful in event handlers that receive an element and need to know which region it belongs to.
apex.region.findClosest(pTarget) → region | null
// the region that contains a cell of the report
const cell = apex.jQuery("#customers td").first();
const region = apex.region.findClosest(cell);
console.log(region.type, region.element.attr("id"));
console.log(apex.region.findClosest("#t_Header")); // not inside a regionOutput:
InteractiveReport customers null
A single click handler on the document can use this to serve every report on the page, rather than wiring up one handler per region.
apex.region.create
Creates a region interface. Region plug-ins use it to give themselves one, but you can also use it to give any region, even a static one, behavior of your own. The properties and methods of the object you pass are copied into the interface. Give it at least a type, plus whichever methods the region supports, such as refresh and focus.
apex.region.create(pRegionId, pRegionImpl)
Here the home page's static welcome region gets a type and a refresh method, so apex.region("welcome").refresh() now works like any other region's:
// give the static Welcome region a region interface of its own
let refreshed = 0;
apex.region.create("welcome", {
type: "WelcomeBanner",
refresh: function () {
refreshed += 1;
this.element.find(".t-Region-title").text("Welcome back (" + refreshed + ")");
return Promise.resolve();
}
});
const welcome = apex.region("welcome");
console.log(welcome.type);
await welcome.refresh();
console.log(welcome.element.find(".t-Region-title").text());Output:
WelcomeBanner Welcome back (1)
A region plug-in calls create from the JavaScript its render function outputs, using the DOM ID it gets from p_region.dom_id. Call it once per region; calling it again for the same region replaces the interface. The payoff is that any code calling apex.region(id).refresh() works on your custom region exactly as it does on the built-in ones.
apex.region.destroy
Removes an interface that create added. The region's element stays on the page; only the interface goes. A plug-in calls it when it removes its region from the page while the page is still open.
apex.region.destroy(pRegionId)
apex.region.create("welcome", { type: "WelcomeBanner" });
console.log("before:", apex.region.isRegion("welcome"));
apex.region.destroy("welcome");
console.log("after: ", apex.region.isRegion("welcome"), "- element still there:", $x("welcome") !== false);Output:
before: true after: false - element still there: true
Recipes
Refresh a Region on a Timer
A dashboard region should stay current without the user reloading the page, but refreshing a region in a background tab nobody is looking at wastes a server request every time. This function refreshes a region on an interval, skips the refresh while the tab is hidden, and shows the time of the last refresh after the region title. It returns a function that stops the timer.
Put the function in the page's Function and Global Variable Declaration attribute and the call in Execute when Page Loads. The last part of the code, marked Try it, only demonstrates the function with a shorter interval.
// === Page › Function and Global Variable Declaration ===
// Refreshes a region every few seconds while the browser tab is visible, and shows when it
// was last refreshed after the region's title. Returns a function that stops it.
function autoRefresh(regionId, seconds) {
const region = apex.region(regionId);
const stamp$ = $("<small class='u-color-text-muted'></small>")
.appendTo(region.element.find(".t-Region-title").first());
const timer = setInterval(async () => {
if (document.visibilityState !== "visible") return; // not while the tab is hidden
await region.refresh(); // a promise
stamp$.text(" · updated " + new Date().toLocaleTimeString("en-US"));
}, seconds * 1000);
return () => clearInterval(timer);
}
// === Page › Execute when Page Loads ===
const stopRefresh = autoRefresh("recent_orders", 60);
// === Try it: refresh every 2 seconds instead, and stop after three refreshes ===
stopRefresh();
const stop = autoRefresh("recent_orders", 2);
const title$ = apex.region("recent_orders").element.find(".t-Region-title").first();
for (let second = 1; second <= 6; second++) {
await new Promise((resolve) => setTimeout(resolve, 1000));
if (second % 2 === 0) console.log(`after ${second} s:`, title$.text());
}
stop();Output:
after 2 s: Recent Orders · updated 7:31:45 PM after 4 s: Recent Orders · updated 7:31:47 PM after 6 s: Recent Orders · updated 7:31:49 PM
The recipe waits for the promise that refresh returns, which works because the region is a template component report. For a region that returns no promise, such as an interactive report, update the time in an apexafterrefresh handler instead. The timestamp is a small element with a class rather than an inline style, because the test application has a Content Security Policy that blocks style attributes added by JavaScript. For a declarative approach with the same goal, see how to auto-refresh a report after a specified interval.
Show a Loading Indicator over a Region
When a slow Ajax call is running, a spinner over the region it will update tells the user something is happening and stops them clicking again. apex.server.process draws one for you when you pass the loadingIndicator option, the element to cover, and loadingIndicatorPosition. It removes the spinner when the response arrives or the call fails.
Put the function in Function and Global Variable Declaration and call it from a dynamic action or other page code. ORDER_SUMMARY is an Ajax Callback process on the test page that returns an order's details as JSON; x02 asks it to wait two seconds so the spinner is visible. Replace it with a callback of your own.
// === Page › Function and Global Variable Declaration ===
// Calls the ORDER_SUMMARY Ajax callback with a spinner over a region until the answer arrives.
function loadSummary(orderId, regionId) {
return apex.server.process("ORDER_SUMMARY", { x01: orderId, x02: 2 }, { // x02: wait 2 seconds
loadingIndicator: apex.region(regionId).element,
loadingIndicatorPosition: "centered"
});
}
// === Try it: load the summary of order 2277 over the Recent Orders region ===
const request = loadSummary(2277, "recent_orders");
await new Promise((resolve) => setTimeout(resolve, 500));
console.log("spinner while waiting:", $("#recent_orders .u-Processing").length);
const summary = await request;
console.log(summary.orderNumber, summary.customer, summary.total);
console.log("spinner afterwards:", $("#recent_orders .u-Processing").length);Output:
spinner while waiting: 1 ORD-12277 Prairie Travel Co. 11495.93 spinner afterwards: 0
loadingIndicatorPosition also accepts "before", "after", "append", "prepend", and "page". For work that is not an Ajax call, apex.util.showSpinner shows the same spinner, as covered in the apex.util.showSpinner example.
Conclusion
The region interface is the common handle for every region in Oracle APEX that has behavior of its own. type and widgetName tell you what you are dealing with, and filterRegionId and parentRegionId tell you how it relates to other regions. refresh reloads it, but only some region types return a promise, so wait for apexafterrefresh when the promise is missing. focus puts the user back in the data. For widget-based regions, widget and call reach the widget's methods, and on and off manage its events, always with the full event name. apex.region.isRegion and findClosest locate regions, and create and destroy let a plug-in, or your own code, give any region an interface that other scripts can use like a built-in one.
