An important aspect when building a web application is navigation. Coming up with a clear and effective navigation system significantly increases the application’s usability. End users should be able to find their way around intuitively and quickly. I have found this to be a challenging task sometimes, especially when dealing with a large application.

In versions prior to Oracle APEX 5.0, we were supposed to create navigation menus based on tabs. I think we can all agree that the tabs system had more limitations than features. That’s why most people preferred lists over tabs to build their navigation menus. This led to the deprecation of tabs in APEX 5.0 and the use of lists as source for navigation components.

The list-based approach for building navigation menus is perfectly fine, but I was in need of something new and fresh. An alternative way of navigating through an application. I came up with a solution that is similar to Mac’s Spotlight Search. My solution is not as powerful as Spotlight Search of course, but it follows the same idea:

Easily navigate from one place to anywhere in the application by only typing in the thing you are looking for.

It’s somehow comparable to a massive Ctrl+F that starts searching through the entire application. Ctrl+F is actually one of my most used features in a browser. It shows me exactly what I am looking for on a page with lots of information, simply by entering a piece of text. So simple, yet so powerful.

I’ve made an attempt to introduce the Spotlight-like functionality in the APEX Sample Database Application. My solution is based on Twitter’s typeahead.js autocomplete JavaScript library. I have setup a live demo application on Sign in with username and password demo/demo. After authentication, click the Quick Jump navigation bar entry (upper right corner). This will open an inline dialog from where you can search for application pages, customers, products, and orders – all from one place.

quick jump navigation
quick jump navigation

In the rest of this article, I’ll explain the steps I’ve gone through to add the Quick Jump Navigation functionality to the Sample Database Application. I did simplify some things, but you can always download the demo application if you’re interested in the complete code.

1. Create a region on Page Zero / Global Page
  • Title: Quick Jump Navigation
  • Template: Inline Dialog
  • Template Options:
    • Dialog Size: Large (720×480)
  • Static ID: qjnModal
2. Create a page item under the Quick Jump Navigation region
  • Name: P0_QJN_ITEM
  • Type: Text Field
  • Label Column Span: 0
  • Template: Hidden
  • Template Options:
    • General: Stretch Form Item
    • Size: X Large
  • Value Placeholder: What are you looking for?
3. Add a navigation bar entry
  • Go to Shared Components > Navigation Bar ListHeader Quick Nav.
  • Click the Create List Entry button.
    • Sequence: 25
    • Image/Class: fa-bolt
    • List Entry Label: Quick Jump
    • Target type: URL
    • URL Target:

Your application should now include a Quick Jump entry in the navigation bar. This means that the Quick Jump Navigation dialog is accessible from all pages.

4. Include the typeahead.js JavaScript library

I downloaded the typeahead.bundle.min.js file from a forked repository. The official typeahead.js repository is pretty much dead and includes some important unfixed bugs, so I wouldn’t recommend downloading the files from there.

Once downloaded, upload the typeahead.bundle.min.js file to your static application/workspace files or web server. In this example, I’ll assume the file has been uploaded as a static application file.

We have to make sure that the JavaScript file gets included on every page in the application.

  • Go to Shared Components > User Interface Attributes.
  • Edit the Desktop UI – we don’t care about the Mobile UI in this example.
  • Under the JavaScript section, reference the typeahead.bundle.min.js file in the File URLs field.
5. Include some custom CSS code

We also have to include some custom CSS code to style the typeahead.js component. Put the below CSS code in a local file on your computer and upload it in the same way as you uploaded the typeahead.bundle.min.js file. Then, go back to the Desktop User Interface Attributes and reference the file in the File URLs field under the Cascading Style Sheets section.

#qjnModal span.twitter-typeahead, .tt-hint, .tt-input, .tt-menu {
  width: 100%;

#qjnModal span.twitter-typeahead .tt-menu {
  position: static !important;
  margin-top: 8px;
  padding: 5px 0;
  border: 1px solid #e7e7e7;
  max-height: 325px;
  overflow-y: auto;

#qjnModal span.twitter-typeahead .tt-dataset-header {
  margin: 0 20px 5px 20px;
  padding: 3px 0;
  border-bottom: 1px solid #e7e7e7;
  font-size: 18px;
  line-height: 24px;
  font-weight: bold;
  color: #333333;

#qjnModal span.twitter-typeahead .tt-suggestion,
#qjnModal {
  padding: 3px 38px;
  font-size: 16px;
  line-height: 21px;
  color: #3d3d3d;

#qjnModal span.twitter-typeahead,
#qjnModal span.twitter-typeahead .tt-suggestion:hover,
#qjnModal span.twitter-typeahead .tt-suggestion:focus {
  color: #ffffff;
  text-decoration: none;
  outline: 0;
  background-color: #2578cf;
  cursor: pointer;
6. Create an application process for each dataset

The typeahead.js library supports the use of multiple datasets. This means that when you enter a search term, typeahead.js will start looking for results in different places. In the Sample Database Application for example, I defined four different datasets: application pages, customers, products, and orders. It is this feature of typeahead.js that gives us the ability to search the entire application from one place.

typeahead.js expects a location from where it can load JSON data for the datasets with AJAX. I have chosen for On Demand application processes in APEX to return the JSON data. The application process takes two parameters: the search term and the maximum amount of rows to return.

  • Process Point: On Demand: Run this application process when requested by a page process.
  • PL/SQL Code:
  lco_search_term constant varchar2(512) := trim(apex_application.g_x01);
  lco_max_rows constant number := apex_application.g_x02;
  lrc_app_pages sys_refcursor;
  open lrc_app_pages for
    select page_name as "page_name",
             p_url => 'f?p=' || :APP_ID || ':' || page_id || ':'|| :APP_SESSION,
             p_checksum_type => 'SESSION'
           ) as "page_url"
      select app_pages.page_id,
      from apex_application_pages app_pages
      join apex_appl_user_interfaces app_ui
      on app_ui.user_interface_id = app_pages.user_interface_id
      where app_pages.application_id = :APP_ID
      and app_ui.ui_type_name = 'DESKTOP'
      and app_pages.page_mode = 'Normal'
      and fun_typeahead_search(app_pages.page_name, lco_search_term) = 1
      and app_pages.page_id not in (0)
      order by app_pages.page_id
    where rownum <= lco_max_rows;


Notice the FUN_TYPEAHEAD_SEARCH function on line 21. This function mimics the search algorithm of typeahead.js. In the above example, a match is made when all of the words in the search term match the beginning of one of the words in the page name. Here’s how the FUN_TYPEAHEAD_SEARCH function looks like:

create or replace function fun_typeahead_search(
  in_search_value in varchar2,
  in_search_term in varchar2
) return number is
  laa_words_in_search_term apex_application_global.vc_arr2;
  laa_words_in_search_term := apex_util.string_to_table(
                                p_string => in_search_term,
                                p_separator => ' '

  for i in 1 .. laa_words_in_search_term.count loop
    if not regexp_like(in_search_value, '(^|\s)' || laa_words_in_search_term(i) || '(*)', 'i') then
      return 0;
    end if;
  end loop;

  return 1;
end fun_typeahead_search;
7. Invoke typeahead.js

Before we invoke typeahead.js, we’ll first define a Bloodhound object to take care of efficient data fetching.

Bloodhound is the typeahead.js suggestion engine. Bloodhound is robust, flexible, and offers advanced functionalities such as prefetching, intelligent caching, fast lookups, and backfilling with remote data.

Bloodhound‘s advanced functionalities are truly an added value in combination with typeahead.js. I won’t go into detail here, but Bloodhound improves the intelligence and usability of typeahead.js by limiting the amount of (slow) remote AJAX calls. I wasn’t a big fan of Bloodhound in the beginning, but I changed my mind as soon as I understood its purposes.

function serializeAjaxReqFormData(pSearchTerm, pMaxRows, pAppProcess) {
  var formData = {
    p_flow_id: $v('pFlowId'),
    p_flow_step_id: $v('pFlowStepId'),
    p_instance: $v('pInstance'),
    x01: pSearchTerm,
    x02: pMaxRows,
    p_request: 'APPLICATION_PROCESS=' + pAppProcess

  return $.param(formData);

$(document).ready(function() {
  var bhAppPages = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('page_name'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    sufficient: 5,
    prefetch: {
      url: '',
      cacheKey: 'QJN_APP_PAGES',
      thumbprint: $v('pInstance'),
      prepare: function(settings) {
                 settings.type = 'POST';
                 settings.contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
        = serializeAjaxReqFormData('', 50, 'QJN_APP_PAGES');
                 return settings;
    remote: {
      url: '',
      prepare: function(query, settings) {
                 settings.type = 'POST';
                 settings.contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
        = serializeAjaxReqFormData(query, 5, 'QJN_APP_PAGES');
                 return settings;

    highlight: true,
    hint: false,
    minLength: 0,
    name: 'QJN_APP_PAGES',
    display: 'page_name',
    source: bhAppPages,
    limit: 5,
    templates: {
      header: '<div class="tt-dataset-header">Application Pages</div>',
      notFound: '<div class="tt-no-results-found">No pages found</div>'
  }).on('typeahead:select', function(obj, datum) {

I’m not going to explain the above code because I feel like this blog post is getting too lengthy. Take a look at the typeahead.js and Bloodhound documentation pages for more info on the used options.

Leave me a comment if you want an in-depth explanation on how typeahead.js, Bloodhound and Oracle APEX work together. Depending on the amount of comments, I’ll write another blog post with a more technical explanation.