Custom Right-Click (Context) Menu | Context Menu Html Css Javascript

Custom Right-Click (Context) Menu | Context Menu Html Css Javascript

Custom Right-Click (Context) Menu | Context Menu Html Css Javascript


Welcome🎉 to Code With Random blog. In this blog, we learn how we create a  Custom Right-Click (Context) Menu. We use HTML, Css, and javascript for this Custom Right-Click (Context) Menu. Hope you enjoy our blog so let's start with a basic HTML structure for a  Custom Right-Click (Context) Menu.

HTML code

 <div class="video-bg">  
  <video width="100%" height="100%" muted autoplay loop>  
   <source src="https://assets.codepen.io/344846/cm.mp4" type="video/mp4">  
  </video>  
 </div>  
 <div class="right-click">Right-click anywhere</div>  
 <div class="target-light target">Light Menu</div>  
 <div class="target-dark target">Dark Menu</div>  
There is all HTML code for the  Custom Right-Click (Context) Menu. Now, you can see output without CSS, then we write css & javascript for the  Custom Right-Click (Context) Menu.
output
Custom Right-Click (Context) Menu | Context Menu Html Css Javascript

CSS Code

 @import url("https://fonts.googleapis.com/css2?family=Inter&display=swap");  
  * {  
       box-sizing: border-box;  
       font-family: "Inter", sans-serif;  
 }  
  html, body {  
       width: 100%;  
       height: 100%;  
       overflow: hidden;  
 }  
  .video-bg {  
       width: 100%;  
       height: 100%;  
       pointer-events: none;  
 }  
  .video-bg video {  
       width: 100%;  
       height: 100%;  
       object-fit: cover;  
 }  
  .target {  
       width: 50%;  
       height: 100%;  
       position: absolute;  
       top: 0;  
       z-index: 1;  
       display: flex;  
       align-items: center;  
       justify-content: center;  
       color: rgba(255, 255, 255, 0.5);  
       font-size: 2vw;  
 }  
  .target-light {  
       left: 0;  
 }  
  .target-dark {  
       right: 0;  
 }  
  body {  
       width: 100%;  
       height: 100%;  
       background-color: #000;  
       overflow: hidden;  
 }  
  .right-click {  
       position: absolute;  
       top: 50%;  
       left: 50%;  
       transform: translate(-50%, -50%);  
       z-index: 2;  
       pointer-events: none;  
       padding: 2vw;  
       border-radius: 1vw;  
       font-size: 2.4vw;  
       background-color: #fff;  
 }  
 /* Context Menu */  
  .contextMenu {  
       --menu-border: rgba(255, 255, 255, 0.08);  
       --menu-bg: linear-gradient(45deg, rgba(10, 20, 28, 0.2) 0%, rgba(10, 20, 28, 0.7) 100%);  
       --item-border: rgba(255, 255, 255, 0.1);  
       --item-color: #fff;  
       --item-bg-hover: rgba(255, 255, 255, 0.1);  
       height: 0;  
       overflow: hidden;  
       background: var(--menu-bg);  
       backdrop-filter: blur(5px);  
       position: fixed;  
       top: var(--top);  
       left: var(--left);  
       animation: menuAnimation 0.4s 0s both;  
       transform-origin: left;  
       list-style: none;  
       margin: 4px;  
       padding: 0;  
       display: flex;  
       flex-direction: column;  
       z-index: 999999999;  
       box-shadow: 0 0 0 1px var(--menu-border), 0 2px 2px #000, 0 4px 4px #000, 0 10px 8px #000, 0 15px 15px #000, 0 30px 30px #000, 0 70px 65px #000;  
 }  
  .contextMenu-item {  
       padding: 4px;  
 }  
  .contextMenu-item[data-divider="top"] {  
       border-top: 1px solid;  
 }  
  .contextMenu-item[data-divider="bottom"] {  
       border-bottom: 1px solid;  
 }  
  .contextMenu-item[data-divider="top-bottom"] {  
       border-top: 1px solid;  
       border-bottom: 1px solid;  
 }  
  .contextMenu-item[data-divider] {  
       border-color: var(--item-border);  
 }  
  .contextMenu-button {  
       color: var(--item-color);  
       background: 0;  
       border: 0;  
       white-space: nowrap;  
       width: 100%;  
       border-radius: 4px;  
       padding: 6px 24px 6px 7px;  
       text-align: left;  
       display: flex;  
       align-items: center;  
       font-size: 14px;  
       width: 100%;  
       animation: menuItemAnimation 0.2s 0s both;  
       font-family: "Inter", sans-serif;  
       cursor: pointer;  
 }  
  .contextMenu-button:hover {  
       background-color: var(--item-bg-hover);  
 }  
  .contextMenu[data-theme="light"] {  
       --menu-bg: linear-gradient(45deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0.85) 100%);  
       --menu-border: rgba(0, 0, 0, 0.08);  
       --item-border: rgba(0, 0, 0, 0.1);  
       --item-color: #0a141c;  
       --item-bg-hover: rgba(10, 20, 28, 0.09);  
 }  
  @keyframes menuAnimation {  
       0% {  
            opacity: 0;  
            transform: scale(0.5);  
      }  
       100% {  
            height: var(--height);  
            opacity: 1;  
            border-radius: 8px;  
            transform: scale(1);  
      }  
 }  
  @keyframes menuItemAnimation {  
       0% {  
            opacity: 0;  
            transform: translateX(-10px);  
      }  
       100% {  
            opacity: 1;  
            transform: translateX(0);  
      }  
 }  

Here is our updated output CSS.

output
Custom Right-Click (Context) Menu | Context Menu Html Css Javascript

Javascript code 

 class ContextMenu {  
  constructor({ target = null, menuItems = [], mode = "dark" }) {  
   this.target = target;  
   this.menuItems = menuItems;  
   this.mode = mode;  
   this.targetNode = this.getTargetNode();  
   this.menuItemsNode = this.getMenuItemsNode();  
   this.isOpened = false;  
  }  
  getTargetNode() {  
   const nodes = document.querySelectorAll(this.target);  
   if (nodes && nodes.length !== 0) {  
    return nodes;  
   } else {  
    console.error(`getTargetNode :: "${this.target}" target not found`);  
    return [];  
   }  
  }  
  getMenuItemsNode() {  
   const nodes = [];  
   if (!this.menuItems) {  
    console.error("getMenuItemsNode :: Please enter menu items");  
    return [];  
   }  
   this.menuItems.forEach((data, index) => {  
    const item = this.createItemMarkup(data);  
    item.firstChild.setAttribute(  
     "style",  
     `animation-delay: ${index * 0.08}s`  
    );  
    nodes.push(item);  
   });  
   return nodes;  
  }  
  createItemMarkup(data) {  
   const button = document.createElement("BUTTON");  
   const item = document.createElement("LI");  
   button.innerHTML = data.content;  
   button.classList.add("contextMenu-button");  
   item.classList.add("contextMenu-item");  
   if (data.divider) item.setAttribute("data-divider", data.divider);  
   item.appendChild(button);  
   if (data.events && data.events.length !== 0) {  
    Object.entries(data.events).forEach((event) => {  
     const [key, value] = event;  
     button.addEventListener(key, value);  
    });  
   }  
   return item;  
  }  
  renderMenu() {  
   const menuContainer = document.createElement("UL");  
   menuContainer.classList.add("contextMenu");  
   menuContainer.setAttribute("data-theme", this.mode);  
   this.menuItemsNode.forEach((item) => menuContainer.appendChild(item));  
   return menuContainer;  
  }  
  closeMenu(menu) {  
   if (this.isOpened) {  
    this.isOpened = false;  
    menu.remove();  
   }  
  }  
  init() {  
   const contextMenu = this.renderMenu();  
   document.addEventListener("click", () => this.closeMenu(contextMenu));  
   window.addEventListener("blur", () => this.closeMenu(contextMenu));  
   document.addEventListener("contextmenu", (e) => {  
    this.targetNode.forEach((target) => {  
     if (!e.target.contains(target)) {  
      contextMenu.remove();  
     }  
    });  
   });  
   this.targetNode.forEach((target) => {  
    target.addEventListener("contextmenu", (e) => {  
     e.preventDefault();  
     this.isOpened = true;  
     const { clientX, clientY } = e;  
     document.body.appendChild(contextMenu);  
     const positionY =  
      clientY + contextMenu.scrollHeight >= window.innerHeight  
       ? window.innerHeight - contextMenu.scrollHeight - 20  
       : clientY;  
     const positionX =  
      clientX + contextMenu.scrollWidth >= window.innerWidth  
       ? window.innerWidth - contextMenu.scrollWidth - 20  
       : clientX;  
     contextMenu.setAttribute(  
      "style",  
      `--width: ${contextMenu.scrollWidth}px;  
      --height: ${contextMenu.scrollHeight}px;  
      --top: ${positionY}px;  
      --left: ${positionX}px;`  
     );  
    });  
   });  
  }  
 }  
 const copyIcon = `<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" stroke-width="2.5" style="margin-right: 7px" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;  
 const cutIcon = `<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" stroke-width="2.5" style="margin-right: 7px" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><circle cx="6" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><line x1="20" y1="4" x2="8.12" y2="15.88"></line><line x1="14.47" y1="14.48" x2="20" y2="20"></line><line x1="8.12" y1="8.12" x2="12" y2="12"></line></svg>`;  
 const pasteIcon = `<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" stroke-width="2.5" style="margin-right: 7px; position: relative; top: -1px" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>`;  
 const downloadIcon = `<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" stroke-width="2.5" style="margin-right: 7px; position: relative; top: -1px" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;  
 const deleteIcon = `<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" stroke-width="2.5" fill="none" style="margin-right: 7px" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>`;  
 const menuItems = [  
  {  
   content: `${copyIcon}Copy`,  
   events: {  
    click: (e) => console.log(e, "Copy Button Click")  
    // mouseover: () => console.log("Copy Button Mouseover")  
    // You can use any event listener from here  
   }  
  },  
  { content: `${pasteIcon}Paste` },  
  { content: `${cutIcon}Cut` },  
  { content: `${downloadIcon}Download` },  
  {  
   content: `${deleteIcon}Delete`,  
   divider: "top" // top, bottom, top-bottom  
  }  
 ];  
 const light = new ContextMenu({  
  target: ".target-light",  
  mode: "light", // default: "dark"  
  menuItems  
 });  
 light.init();  
 const dark = new ContextMenu({  
  target: ".target-dark",  
  menuItems  
 });  
 dark.init();  
 // remove message  
 function removeMessage() {  
  const message = document.querySelector(".right-click");  
  if (message) message.remove();  
 }  
 window.addEventListener("click", removeMessage);  
 window.addEventListener("contextmenu", removeMessage);  
Final output
Custom Right-Click (Context) Menu | Context Menu Html Css Javascript


Custom Right-Click (Context) Menu | Context Menu Html Css Javascript

Now we have completed our javascript section,  Here is our updated output with javascriptHope you like the  Custom Right-Click (Context) Menu. you can see output video and project screenshots. See our other blogs and gain knowledge in front-end development. Thank you 🙏💕!

Post a Comment

Previous Post Next Post