Thursday, September 10, 2026
HomejQueryCustomized High-Layer Context Menu Plugin for jQuery and JavaScript

Customized High-Layer Context Menu Plugin for jQuery and JavaScript


@ozankurt/context-menu is a JavaScript UI library that renders customized right-click context menus into the browser prime layer.

The library mounts floating panels utilizing the HTML Popover API to stop container clipping from overflow: hidden, coordinate distortion from CSS transforms, and z-index collisions.

It runs as a zero-dependency TypeScript engine with devoted adapters for jQuery, React, and Vue.

Options

  • Zero runtime dependencies within the core library.
  • jQuery, React, Vue, and vanilla JavaScript entry factors.
  • One delegated listener for selector-based menu registrations.
  • High-layer menu placement by the browser Popover API.
  • Nested submenus with synchronous or asynchronous merchandise lists.
  • Normal actions, separators, headings, checkboxes, radio selections, and customized content material.
  • Keyboard navigation, type-ahead search, and contact lengthy press.
  • Conditional hidden and disabled actions from aspect metadata.
  • Effervescent DOM occasions for framework-independent lifecycle dealing with.
  • CSS customized properties, darkish themes, RTL format, and reduced-motion dealing with.

How To Use It

jQuery

Load the stylesheet first, adopted by jQuery and the worldwide context menu construct.


<hyperlink
  rel="stylesheet"
  href="https://unpkg.com/@ozankurt/context-menu/dist/kinds.css"
>

<script src="/path/to/cdn/jquery.min.js"></script>
<script src="https://unpkg.com/@ozankurt/context-menu/dist/context-menu.world.js"></script>

If jQuery masses after the context menu construct, register the adapter as soon as:


ContextMenu.registerJQueryPlugin(jQuery);

For package-based initiatives:


npm set up @ozankurt/context-menu jquery

import $ from 'jquery';
import { registerJQueryPlugin } from '@ozankurt/context-menu/jquery';
import '@ozankurt/context-menu/kinds.css';

registerJQueryPlugin($);

Primary Utilization

Add the weather that ought to open the context menu:


<div
  class="document-row"
  data-document-id="42"
  data-title="Quarterly finances.xlsx"
  data-locked="false"
>
  Quarterly finances.xlsx
</div>

<div
  class="document-row"
  data-document-id="57"
  data-title="Contract.pdf"
  data-locked="true"
>
  Contract.pdf
</div>

Initialize the plugin:


$('.document-row').contextMenu({
  header: perform (meta) {
    return meta.title;
  },

  objects: [
    {
      label: 'Open',
      hint: 'Enter',
      action: function (meta) {
        console.log('Open document', meta.documentId);
      }
    },
    {
      label: 'Rename',
      disabled: function (meta) {
        return meta.locked === true;
      },
      action: function (meta) {
        console.log('Rename document', meta.documentId);
      }
    },
    { type: 'separator' },
    {
      label: 'Delete',
      variant: 'danger',
      disabled: function (meta) {
        return meta.locked === true;
      },
      action: function (meta) {
        console.log('Delete document', meta.documentId);
      }
    }
  ]
});

Open or destroy the menu programmatically when wanted:


// Open on the aspect edge.
$('.document-row').contextMenu('open');

// Open at coordinates from a mouse occasion.
$('.document-row').contextMenu('open', occasion);

// Take away the registration.
$('.document-row').contextMenu('destroy');

Vanilla JavaScript

Browser Script

Load the CSS and world JavaScript construct:


<hyperlink
  rel="https://unpkg.com/@ozankurt/context-menu/dist/kinds.css"
>

<script src="https://unpkg.com/@ozankurt/context-menu/dist/context-menu.world.js"></script>

Add any parts you need to match:


<button
  class="project-row"
  sort="button"
  data-id="12"
  data-name="Shopper Portal"
  data-locked="false"
>
  Shopper Portal
</button>

Create a menu and register a CSS selector:


var menu = ContextMenu.createContextMenu();

menu.register({
  on: '.project-row',

  header: perform (meta) {
    return meta.identify;
  },

  objects: [
    {
      label: 'Open project',
      action: function (meta) {
        console.log('Open project', meta.id);
      }
    },
    {
      label: 'Rename',
      disabled: function (meta) {
        return meta.locked === 'true';
      }
    },
    { type: 'separator' },
    {
      label: 'Delete',
      variant: 'danger',
      action: function (meta) {
        console.log('Delete project', meta.id);
      }
    }
  ]
});

Provide resolveMeta when software code wants typed values or richer state:


menu.register({
  on: '.project-row',

  resolveMeta: perform (el) {
    return {
      id: Quantity(el.dataset.id),
      identify: el.dataset.identify,
      locked: el.dataset.locked === 'true'
    };
  },

  objects: [
    {
      label: 'Rename',
      disabled: function (meta) {
        return meta.locked;
      }
    }
  ]
});

For one particular aspect, use connect():


var dispose = menu.connect(
  doc.querySelector('#project-actions'),
  {
    objects: [
      {
        label: 'Open settings',
        action: function () {
          openSettings();
        }
      }
    ]
  }
);

// Take away the registration later.
dispose();

npm


npm set up @ozankurt/context-menu

import { createContextMenu } from '@ozankurt/context-menu';
import '@ozankurt/context-menu/kinds.css';

const menu = createContextMenu();

menu.register({
  on: '.project-row',
  objects: [
    {
      label: 'Open project',
      action: (meta) => console.log(meta.id)
    }
  ]
});

React

Set up the bundle:


npm set up @ozankurt/context-menu

Import the React hook and stylesheet:


import { useContextMenu } from '@ozankurt/context-menu/react';
import '@ozankurt/context-menu/kinds.css';

Cross software information by meta and connect the returned ref to the aspect:


const menuConfig = {
  header: (challenge) => challenge.identify,

  objects: [
    {
      label: 'Open project',
      action: (project) => {
        console.log('Open', project.id);
      }
    },
    {
      label: 'Rename',
      disabled: (project) => project.locked
    },
    { type: 'separator' },
    {
      label: 'Delete',
      variant: 'danger',
      action: (project) => {
        console.log('Delete', project.id);
      }
    }
  ]
};

perform ProjectRow({ challenge }) {
  const { ref } = useContextMenu(
    {
      ...menuConfig,
      meta: challenge
    },
    [project]
  );

  return (
    <button ref={ref} sort="button">
      {challenge.identify}
    </button>
  );
}

The hook additionally returns open and shut for click on buttons or keyboard-driven instructions:


perform ProjectActions({ challenge }) {
  const { ref, open, shut } = useContextMenu(
    {
      ...menuConfig,
      meta: challenge
    },
    [project]
  );

  return (
    <button ref={ref} sort="button" onClick={open}>
      Undertaking actions
    </button>
  );
}

Wrap a part of an software in ContextMenuProvider when it wants customized occasion settings:


import { ContextMenuProvider } from '@ozankurt/context-menu/react';

perform App() {
  return (
    <ContextMenuProvider
      choices={{
        closeOnScroll: false,
        offset: { x: 4, y: 4 }
      }}
    >
      <ProjectList />
    </ContextMenuProvider>
  );
}

For TypeScript parts akin to buttons, move the aspect sort when required:


const { ref } = useContextMenu<HTMLButtonElement>(
  {
    ...menuConfig,
    meta: challenge
  },
  [project]
);

Vue

Set up the bundle:


npm set up @ozankurt/context-menu

The Vue directive is the quickest setup for a component that already has its information object within the template.


<script setup>
import { vContextMenu } from '@ozankurt/context-menu/vue';
import '@ozankurt/context-menu/kinds.css';

defineProps({
  challenge: Object
});

const projectMenu = {
  header: (challenge) => challenge.identify,

  objects: [
    {
      label: 'Open project',
      action: (project) => {
        console.log('Open', project.id);
      }
    },
    {
      label: 'Rename',
      disabled: (project) => project.locked
    },
    { type: 'separator' },
    {
      label: 'Delete',
      variant: 'danger',
      action: (project) => {
        console.log('Delete', project.id);
      }
    }
  ]
};
</script>

<template>
  <button
    sort="button"
    v-context-menu="{ ...projectMenu, meta: challenge }"
  >
    {{ challenge.identify }}
  </button>
</template>

Register the directive throughout the applying with ContextMenuPlugin:


import { createApp } from 'vue';
import { ContextMenuPlugin } from '@ozankurt/context-menu/vue';
import '@ozankurt/context-menu/kinds.css';

import App from './App.vue';

createApp(App)
  .use(ContextMenuPlugin, {
    closeOnScroll: false
  })
  .mount('#app');

The composable works when menu registration will not be tied on to directive markup:


import { ref } from 'vue';
import { useContextMenu } from '@ozankurt/context-menu/vue';

const projectButton = ref(null);

const {
  open,
  shut,
  dispose
} = useContextMenu(
  {
    objects: projectMenu.objects
  },
  {
    goal: projectButton
  }
);

Use on within the second argument for delegated selector binding:


useContextMenu(
  {
    objects: projectMenu.objects
  },
  {
    on: '.project-row'
  }
);

Configuration Choices

ContextMenu Occasion Choices

  • root (HTMLElement | Doc): Delegation root. Default: doc.
  • offset ({ x: quantity, y: quantity }): Horizontal and vertical distance from the anchor. Default: { x: 2, y: 2 }.
  • closeOnScroll (boolean): Closes the menu when an ancestor of the set off scrolls. Default: true.
  • longPress (quantity | false): Contact maintain delay in milliseconds. Default: 500.
  • className (string): Further courses utilized to panels created by the occasion.
  • zIndexFallback (quantity): Fastened-position fallback z-index when Popover is unavailable. Default: 2147483000.

Core Menu Definition

  • on (string): Delegated CSS selector.
  • objects (Merchandise[] | ItemsResolver): Menu objects or a synchronous/asynchronous resolver.
  • resolveMeta ((el, occasion) => Meta): Builds metadata when the menu opens.
  • header (string | (meta, ctx) => string): Textual content above the merchandise checklist.
  • class (string | string[]): Customized courses utilized to the panel.
  • variant (string): Panel-level data-variant worth.
  • offset ({ x: quantity, y: quantity }): Per-menu offset override.
  • precedence (quantity): Resolves definitions that match the similar aspect.

Adapter Fields

React, Vue, and jQuery can move software objects by:

  • meta (Report<string, unknown>): Object handed on to predicates, labels, and actions.

The jQuery adapter additionally accepts:

  • occasion (ContextMenu): Registers parts on a particular ContextMenu occasion.

The Vue plugin accepts:

  • directiveName (string): World directive identify. Default: 'context-menu'.
  • occasion (ContextMenu): Reuses an present ContextMenu occasion.

The Vue composable binding accepts:

  • goal (HTMLElement | Ref | getter): Factor used for direct binding.
  • on (string): Delegated selector.
  • occasion (ContextMenu): ContextMenu occasion used for registration.

Menu Merchandise Choices

  • label (string | predicate): Merchandise textual content.
  • sort ('merchandise' | 'separator' | 'header' | 'checkbox' | 'radio' | 'customized'): Merchandise sort.
  • icon (string): Icon class checklist or trusted markup.
  • variant (string): Merchandise-level variant.
  • class (string | string[]): Customized merchandise courses.
  • trace (string): Proper-aligned helper textual content.
  • disabled (boolean | predicate): Retains the merchandise seen and blocks activation.
  • hidden (boolean | predicate): Removes the merchandise.
  • motion (perform): Runs after choice and menu closure.
  • objects (Merchandise[] | ItemsResolver): Defines a submenu.
  • id (string): Utility merchandise identifier.
  • checked (boolean | predicate): Checkbox state.
  • group (string): Metadata key for radio objects.
  • worth (unknown): Radio worth in contrast towards the group worth.
  • render (perform): Returns an HTMLElement or trusted HTML string for a customized merchandise.

API Strategies


// Create a ContextMenu occasion.
var menu = ContextMenu.createContextMenu({
  offset: { x: 4, y: 4 }
});

// Create an occasion by the category.
var secondMenu = new ContextMenu.ContextMenu({
  longPress: 650
});

// Register a delegated definition.
// Returns a disposer.
var disposeRegistration = menu.register({
  on: '.record-row',
  objects: [
    { label: 'Open record' }
  ]
});

// Connect one aspect.
// Returns a disposer.
var disposeElement = menu.connect(
  doc.querySelector('#record-17'),
  {
    objects: [
      { label: 'Edit record' }
    ]
  }
);

// Open a menu.
// Returns Promise<void>.
menu.open(
  { x: 180, y: 120 },
  {
    on: '.record-row',
    objects: [
      { label: 'Open record' }
    ]
  },
  doc.querySelector('.record-row')
);

// Shut the energetic menu.
// Default cause: "api".
menu.shut();

// Subscribe to an occasion.
// Returns an unsubscribe perform.
var offOpen = menu.on('open', perform (occasion) {
  console.log(occasion.meta);
});

// Learn the open state.
console.log(menu.isOpen);

// Destroy the occasion.
menu.destroy();

// Take away registrations or subscriptions.
disposeRegistration();
disposeElement();
offOpen();

// Set up the jQuery adapter manually.
ContextMenu.registerJQueryPlugin(jQuery);

// jQuery adapter instructions.
$('.record-row').contextMenu({
  objects: [
    { label: 'Open record' }
  ]
});

$('.record-row').contextMenu('open');
$('.record-row').contextMenu('open', occasion);
$('.record-row').contextMenu('destroy');

Occasions


doc.addEventListener('ctxmenu:beforeopen', perform (e) {
  console.log(e.element.meta);
});

doc.addEventListener('ctxmenu:beforeitems', perform (e) {
  console.log(e.element.objects);
});

doc.addEventListener('ctxmenu:open', perform (e) {
  console.log(e.element.el, e.element.meta);
});

doc.addEventListener('ctxmenu:spotlight', perform (e) {
  console.log(e.element.merchandise);
});

doc.addEventListener('ctxmenu:submenu:open', perform (e) {
  console.log(e.element.merchandise);
});

doc.addEventListener('ctxmenu:submenu:shut', perform (e) {
  console.log(e.element.merchandise);
});

doc.addEventListener('ctxmenu:choose', perform (e) {
  console.log(e.element.merchandise);
});

doc.addEventListener('ctxmenu:motion', perform (e) {
  console.log(e.element.merchandise);
});

doc.addEventListener('ctxmenu:error', perform (e) {
  console.error(e.element.error);
});

doc.addEventListener('ctxmenu:beforeclose', perform (e) {
  console.log(e.element.cause);
});

doc.addEventListener('ctxmenu:shut', perform (e) {
  console.log(e.element.cause);
});

jQuery code can hearken to these occasions by .on():


$('.record-row').on('ctxmenu:open', perform (e) {
  console.log(e.originalEvent.element.meta);
});

Alternate options and Associated Sources


This superior jQuery plugin is developed by OzanKurt. For extra Superior Usages, please test the demo web page or go to the official web site.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments