Make a Custom Right Click Menu with Javascript

Video version if you want to see it.
Give it a like 😁

Alright let’s see how to make a custom menu.
We are going to use vanilla javascript, but you can easily use this to make it on react or vue or other framework of your choosing.

This one is going to be short and sweet for those that just wants the result 😁

First we’ll have the menu itself.

  <div class="app" id="app">
    <ul class="menu hide" id="menu">
      <li class="menuItem"><span>🎵</span>Play</li>
      <li class="Separator"></li>
      <li class="menuItem"><span></span>Next</li>
      <li class="menuItem"><span></span>Stop</li>
      <li class="menuItem"><span></span>Last</li>
      <li class="Separator"></li>
      <li class="menuItem"><span></span>Record</li>
      <li class="menuItem"><span>🔄</span>Reload</li>
      <li class="Separator"></li>
      <li class="menuItem"><span>🗑️</span>Discard</li>
Enter fullscreen mode

Exit fullscreen mode

Then we define some helper functions and we select some elements in the DOM.

el = (e) => document.querySelector(e)

const app = el('#app')
const menu = el('#menu')

let menuActive = false

const showMenu = () => {

const hideMenu = () => {
Enter fullscreen mode

Exit fullscreen mode

We have a function to select elements, a flag for the state, then two functions one to show the menu and one to hide it, (we can do this with a toggle function but I wanted to make things more clear, so feel free to implement that)

Now lets see where the important stuff is:

const moveMenu = (event) => {

  menuActive = true

  let wHeight = window.innerHeight

  let scrPosX = event.clientX + window.scrollX
  let scrPosY = event.clientY + window.scrollY

  let Y_window_offset = event.clientY + menu.clientHeight - wHeight

  if (Y_window_offset > 0) {
    scrPosY -= menu.clientHeight
  } = `${scrPosX}px` = `${scrPosY}px`

  return false
Enter fullscreen mode

Exit fullscreen mode

So what we are doing here:

  • First we prevent the default operating system menu
  • We set the menu state menuActive to active
  • We retrieve the windows inner height, this one is the height of the viewport
  • Now we calculate the coordinates of the menu click and we offset them with the scroll from both Y and X axis
  let scrPosX = event.clientX + window.scrollX
  let scrPosY = event.clientY + window.scrollY
Enter fullscreen mode

Exit fullscreen mode

  • Now this one let Y_window_offset = event.clientY + menu.clientHeight - wHeight is the difference between the bottom of the menu and the bottom of the screen, we’ll use this to know if the menu is going offscreen at the bottom and we can shift it up
  • We do that when that offset is positive and we substract that offset with the height of the menu.
  • Finally we set the left and top CSS positions of the menu elemnt to those coordinates, since the element is in position absolute it will move to those coordinates, note that the parent is in position relative.

Then we give this functions to the contextmenu event listener and whoala:

app.addEventListener('contextmenu', moveMenu)
app.addEventListener('click', (event) => {
  if (menuActive) {
    menuActive = false

Enter fullscreen mode

Exit fullscreen mode

We also add a event listener for a click in the app for hiding the menu.

So if you want to make this even better you can do the same shift we do for the bottom of the screen but at the right side too, because now the menu will go offscreen 😅

I will be making better content now that I have some better equipment, going to make Machine Learning, app development, Data science and General programming stuff content 😁
I’ll appreciate if you give me a follow, also if you have time check out my youtube channel ramgendeploy drop a sub if you like there 😄

CSS for the braves (jk)

@import url("");
* {
  box-sizing: content-box;

body {
  margin: 0;
  font-family: "Open Sans", sans-serif;
  position: relative;
  background: #151515;

.app {
  background: #202020;
  width: 80%;
  margin: 0 auto;
  height: fit-content;
  color: white;
  padding: 15px;
  line-height: 30px;

.hide {
  display: none;

.show {
  display: block;

.menu {
  margin: 0;
  color: black;
  border: solid black 1px;
  position: absolute;
  width: 250px;
  background: white;
  padding: 10px 0px;
  border-radius: 4px;

.Separator {
  display: block;
  height: 1px;
  background: #7e7b7b;
  margin: 5px 10px;
  user-select: none;

.menuItem {
  display: grid;
  grid-template-columns: 30px auto;
  align-items: center;
  padding: 7px 15px 7px 8px;
  user-select: none;
  cursor: pointer;

.menuItem:hover {
  background: lightblue;
  transition: 500ms;

.menuItem:active {
  background: cadetblue;
  transition: 500ms;

.imgL {
  float: left;
  padding: 10px;

.imgR {
  float: right;
  padding: 10px;

.slime {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background: #202020;
  padding: 10px;
  border-bottom: 3px solid #303030;
Enter fullscreen mode

Exit fullscreen mode


Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

GIPHY App Key not set. Please check settings

Structure Guided Lane Detection with python

Daypaper sets your GNOME wallpaper based on the time of day from a random and relevant Unsplash image