Oracle APEX Email Validator Plug-in - Live Validation While You Type

Oracle APEX Plug-in JavaScript Open Source

Email Validator — An Oracle APEX Item Plug-in That Validates While You Type

A free, MIT-licensed item type plug-in with 25 configurable rules, a live popover, domain white- and blocklists, and a button that stays locked until the address is valid.

📅 September 2026 👤 Sajjad Hanifa ⏱ ~8 min read 🏢 S&H Software Solutions

Every application I build has a form with an email field in it. And every single time, the same thing happens: the user types max.mustermann@gmial,com, hits Submit, the page reloads, and a red error banner appears at the top telling them something is wrong. They scroll up, read it, scroll back down, fix one character — and repeat.

The native APEX Email item does not help much here. It renders an <input type="email"> and leaves the rest to the browser, whose built-in check is famously permissive: a@b passes. Anything stricter means writing an APEX validation, and an APEX validation only runs after the page has been submitted. You can bolt a Dynamic Action with some inline JavaScript on top of it, but then that code lives on one page of one application, and the next form starts from zero.

So I packaged the whole thing as an item type plug-in. It drops into any page item, validates every rule in the browser as the user types, and shows only the rules that currently fail. This post walks through what it does, how to install it, what each configuration group is for, and — importantly — why client-side validation alone is never enough.

💡
Free and open source. The plug-in is MIT licensed and available on GitHub: github.com/Sajjad-786/apex-email-validator. Download the .sql export, import it, done — no database objects, no dependencies.
Oracle APEX Email Validator plug-in — live validation popover under the email field
The popover lists only the rules that currently fail — and the button below stays locked until they are all satisfied.

What the Plug-in Does

The core idea is subtractive feedback. Most validators show a static checklist of every rule with green and red icons next to it, which means the user is staring at a wall of text from the first keystroke. This one goes the other way: it lists only what is still wrong. As the address gets better, the box gets shorter — and the moment every active rule passes, the whole list collapses into a single green line.

Live popover — opens on focus, updates on every keystroke, and is attached to <body> so no region with overflow:hidden can clip it
Structure rules — exactly one @, text before and after it, a required top-level domain, no spaces or control characters
Format rules — no consecutive dots, no leading or trailing dot in the local part, no leading or trailing dot or hyphen in the domain
Regex rules — the local part, the domain and the TLD each get their own pattern and their own on/off switch
Length rules — independent minimum and maximum for the complete address
Domain lists — an allow-list for internal applications, a blocklist for disposable providers like mailinator.com
Auto lowercase — normalizes the value while typing, with the caret position preserved
Button control — an optional button stays blocked, with a hover hint, until the field is valid
Bilingual — every message ships in German and English, picked from the browser language, with no APEX translation setup

Everything is off by default. An item with no configuration behaves like a plain text field, so you enable exactly the rules your form needs and nothing more.


01
Step One
Install the Plug-in
App Builder · Import · Plug-in · ~30 seconds

Grab item_type_plugin_email_validator.sql from the repository. It is a component export containing the render procedure and both asset files, so there is nothing else to upload.

In your application go to App Builder → Import
Select the file, set the type to Plug-in, then Next → Install
Check Shared Components → Plug-insEmail validator should now be listed
Open any page item and set Type to Email validator

If you prefer the command line, the same file runs through SQLcl or SQL*Plus when you are connected as the application's parsing schema.

The Email Validator plug-in definition in Oracle APEX Shared Components with all 25 custom attributes
The plug-in definition after the import — 25 custom attributes, grouped by category.
Requirements: Oracle APEX 23.2 or later, Universal Theme, no database objects. The plug-in works on page items and on Interactive Grid columns. Every icon in the popover is drawn in pure CSS, so there is no icon-font dependency either.

02
Step Two
Configure the Rules
7 attribute groups · 25 attributes · all optional

Page Designer shows the attributes in seven groups. Each group is a category of rule, and inside a group the pattern is always the same: a Yes/No switch turns the rule on, and any value the rule needs only appears once that switch is enabled.

GroupWhat it covers
 Button controlThe Static ID of a button that stays locked while the field is invalid
🧱  Basic validationRequired value, auto lowercase, no spaces, exactly one @, text before and after it, required TLD
🔹  Format validationNo double dots, no dot at the edge of the local part, no dot or hyphen at the edge of the domain
🔤  Character validationSeparate regular expressions for the local part, the domain and the TLD
📏  Length validationMinimum and maximum length of the complete address
 Approved domainsOnly accept addresses from a comma-separated list of domains
🚫  Blocked domainsReject addresses from a comma-separated list — disposable providers by default
Oracle APEX Page Designer showing the Email Validator attribute groups on a page item
The attribute groups in Page Designer. Dependent attributes stay hidden until their switch is turned on.

Three configurations that cover most forms

Rather than listing all 25 attributes here, these are the three setups I actually reach for. The full attribute reference with defaults and examples is in the README on GitHub.

configuration — page item attributes
# 1 — Minimal: just make sure it looks like an email address
Required value:   Yes
Only 1 @:         Yes
Text before @:    Yes
Text after @:     Yes
Require TLD:      Yes

# 2 — Public sign-up: clean input, no throwaway addresses
Required value:   Yes
Auto lowercase:   Yes
No spaces:        Yes
Only 1 @:         Yes
No double dots:   Yes
Local edge:       Yes
Domain edge:      Yes
Require TLD:      Yes
Block domains:    Yes   Domain list: mailinator.com, yopmail.com, 10minutemail.com
Min. check:       Yes   Min. length: 6
Max. check:       Yes   Max. length: 100

# 3 — Internal application: company and partner domains only
Required value:   Yes
Restrict access:  Yes   Domain list: company.com, partner.org
Only 1 @:         Yes
Require TLD:      Yes
Button Static ID: NEXT_BTN

A note on plus-addressing

The default pattern for the local part is the standard permitted character set, and it includes the plus sign — so max+newsletter@example.com passes. That is deliberate: those addresses are valid, and plenty of people use them to tag their inbox. If your downstream system cannot cope with them, enable Local check and remove the + from the pattern:

regex — Local regex attribute
^[A-Za-z0-9._-]+$

03
Step Three
Lock the Button Until the Field Is Valid
Static ID · hover hint · aria-disabled

This is the attribute I use most. Give the button a Static ID in Page Designer — say NEXT_BTN — put the same value into the plug-in's Button Static ID attribute, and the button is blocked for as long as the address fails a rule. Hovering it shows a small hint underneath: "Please enter a valid email address first."

Three details in the implementation are worth calling out, because they are the kind of thing that breaks a naive version of this feature:

No disabled property. Browsers suppress mouse events on disabled elements, so a genuinely disabled button never fires the mouseenter the hint depends on. The plug-in uses a CSS class plus aria-disabled instead, and swallows the click in the capture phase so the button's own apex.submit never runs.
Hidden fields do not block. An item sitting in an unopened inline dialog would otherwise lock a button on a page where the user cannot even see the field. A visibility check plus an IntersectionObserver re-runs the validation the moment the field actually appears.
Several validators can share one button. A small shared registry on window collects the messages instead of overwriting the button's title, and the hint boxes stack underneath each other. So an email field and a password field can both guard the same submit button.
javascript — shared button registry
// Several S&H validators can guard the same button: each one
// registers its own message under its item name instead of
// overwriting the button's title attribute.
window.shRegisterBtnBlocker = function (pButtonId, pKey, pMessage) {
  if (!pButtonId) { return; }
  var reg = window.shBtnRegistry;
  reg[pButtonId] = reg[pButtonId] || {};

  if (pMessage) {
    reg[pButtonId][pKey] = pMessage;
  } else {
    delete reg[pButtonId][pKey];
  }

  var button = document.getElementById(pButtonId);
  if (!button) { return; }

  var messages = Object.keys(reg[pButtonId]).map(function (k) {
    return reg[pButtonId][k];
  });

  if (messages.length > 0) {
    button.setAttribute("title", messages.join(" · "));
  } else {
    button.removeAttribute("title");
  }
};
⚠️
A blocked button is not security. Anyone can re-enable it from the browser's developer tools in about four seconds. It is a UX convenience — the real check has to run on the server.

Client-Side Is Comfort, Server-Side Is Truth

This applies to every JavaScript validation, not just this plug-in. Everything the browser does can be bypassed: JavaScript can be disabled, the DOM can be edited, and a request can be replayed with any payload at all. So whatever rules matter to your data, mirror the important ones in an APEX validation of type PL/SQL Function Body (returning Boolean):

sql — APEX validation, PL/SQL function body
DECLARE
    l_email    VARCHAR2(4000) := LOWER(TRIM(:P1_EMAIL));
    l_domain   VARCHAR2(4000);
    l_blocked  apex_t_varchar2 := apex_string.split(
                   'mailinator.com,yopmail.com,10minutemail.com', ',');
BEGIN
    -- Structure: exactly one @, text on both sides, a real TLD
    IF NOT REGEXP_LIKE(l_email, '^[^@[:space:]]+@[^@[:space:]]+\.[A-Za-z]{2,}$') THEN
        RETURN FALSE;
    END IF;

    -- Length of the complete address
    IF LENGTH(l_email) NOT BETWEEN 6 AND 100 THEN
        RETURN FALSE;
    END IF;

    -- Blocked (disposable) domains
    l_domain := SUBSTR(l_email, INSTR(l_email, '@') + 1);

    FOR i IN 1 .. l_blocked.COUNT LOOP
        IF l_domain = TRIM(l_blocked(i)) THEN
            RETURN FALSE;
        END IF;
    END LOOP;

    RETURN TRUE;
END;

The plug-in keeps the user from ever reaching that validation by accident. The validation keeps everyone else out.


How It Is Built

The architecture is deliberately boring, and that is the point — it makes the plug-in easy to read and easy to fork.

The render procedure writes a wrapper <span> whose data-* attributes carry the entire configuration, then the input, then an empty popover skeleton. It never renders any rule text, because the messages are bilingual and the language is only known in the browser:

sql — render procedure (excerpt)
sys.htp.p (
    '<span class="sh-eml-wrapper" id="' || v_element_id || '_SH_EMAIL"'
    || ' data-require="'        || v_require_value  || '"'
    || ' data-single-at="'      || v_single_at      || '"'
    || ' data-require-tld="'    || v_require_tld    || '"'
    || ' data-min-check="'      || v_min_check      || '"'
    || ' data-min-length="'     || NVL(v_min_length, '') || '"'
    || ' data-domain-whitelist="'
    || apex_escape.html_attribute(NVL(v_domain_whitelist, '')) || '">'
);

apex_javascript.add_onload_code (
    p_code => 'shEmailValidator.init("' || v_element_id || '");'
);

On the client each rule is a small object with three keys — whether it is active, the message to show, and the test itself. Adding a rule means adding one entry to the array; nothing else in the file has to change:

javascript — rule definition
{
  active:  cfg.singleAt,
  message: txt("Muss genau ein @-Zeichen enthalten",
               "Must contain exactly one @ symbol"),
  test: function (v) {
    if (v.trim().length === 0) { return true; }
    return (v.match(/@/g) || []).length === 1;
  }
},

On every keystroke the active rules are filtered down to the failing ones, and only those are rendered. Two more things fall out of this design almost for free: the popover is moved to <body> on initialization, so no parent region can clip it or cover it, and user-supplied regular expressions are executed inside a try/catch — a typo in an attribute logs a console warning instead of throwing an error on every keystroke.

🔒
The value never leaves the input. Only the configuration is written into data-* attributes, never what the user typed — exactly like a native APEX item.

⬇️ Download
Email Validator — Oracle APEX Item Plug-in
MIT licensed and free for personal and commercial projects. The repository contains the ready-to-import .sql export, the render procedure in readable form, the source and minified assets, and a full attribute reference in the README.
View on GitHub →

Final Thoughts

The interesting part of building this was not the regular expressions — those are a solved problem. It was the small behaviours around them: not showing the user a wall of rules they have already satisfied, keeping the popover out of the way of overflow:hidden, and making sure a field hidden inside an unopened dialog never locks a button on the visible part of the page. Those are the details that decide whether a plug-in feels finished or merely functional.

A word of thanks to Hassaan Ahmed Tahir, who contributed to this plug-in and to its sibling, the Password Validator. Both of them share the button registry described above, so an email field and a password field can guard the same submit button without fighting over its tooltip.

If you find a bug or have an idea for a rule that is missing, open an issue on GitHub — I read all of them.

SH
Sajjad Hanifa
Software Developer · S&H Software Solutions · Oracle APEX, PL/SQL, Plug-in Development
Oracle APEX APEX Plug-in Item Type Plugin PL/SQL JavaScript Email Validation Open Source

 {fullWidth}

Please Select Embedded Mode To Show The Comment System.*

Previous Post Next Post

نموذج الاتصال