Solution: Resolve clientY scrolling issue within an HTML document

I am facing an issue with the positioning of my context menu. When I right-click and scroll down, the menu does not maintain its regular position. Instead of appearing to the right of the mouse cursor when stationary, it moves below the cursor based on how far I scroll. I have attempted using clientY and scrollY, but these fixes did not resolve the problem. Here is a code snippet showcasing the implementation:

// JavaScript function to display the custom context menu with a sliding effect
function showContextMenu(x, y) {
    var menu = document.querySelector(".ctx-context-menu");
    
    if (menu.classList.contains("ctx-menu-visible")) {
        
        menu.classList.add("ctx-menu-slide");
        menu.style.left = x + "px";
        menu.style.top = y + "px";

        setTimeout(function () {
            menu.classList.remove("ctx-menu-slide");
        }, 500); 
        
    } else {
      
        menu.style.left = x + "px";
        menu.style.top = y + "px";
        menu.classList.add("ctx-menu-visible");
    }
}

// Function to hide the custom context menu
function hideContextMenu() {
    var menu = document.querySelector(".ctx-context-menu");
    
    menu.classList.remove("ctx-menu-visible");
}

document.addEventListener("contextmenu", function (e) {
    var menu = document.getElementById("ctxContextMenu");

    if (menu.contains(e.target)) {
       
        return;
    }
    e.preventDefault(); 
    showContextMenu(e.pageX, e.pageY);
});

document.addEventListener("click", function (e) {
    var menu = document.getElementById("ctxContextMenu");

    if (!menu.contains(e.target)) {
        hideContextMenu();
    }
});

document.getElementById("ctxContextMenu").addEventListener("contextmenu", function (e) {
    e.preventDefault(); 
});

document.addEventListener("keydown", function (e) {
    if (e.key === "Escape") {
        hideContextMenu();
    }
});
/* Context Menu Styles */
.ctx-context-menu {
    opacity: 0;
    position: fixed;
    z-index: 10000;
    width: 150px;
    background-color: #1a1a1a;
    border: 2px solid #ff0000;
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
    border-radius: 10px;
    padding: 10px 0;
    transition: opacity 0.15s linear; 
    pointer-events: none; 
}

.ctx-menu-slide {
    transition: left 0.25s, top 0.25s; 
}

.ctx-menu-visible {
    opacity: 1;
    pointer-events: auto; 
}

.ctx-context-menu-item {
    color: #808080;
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s linear;
}

.ctx-context-menu-item:hover {
    color: #ff00ff;
}
<nav id="ctxContextMenu" class="ctx-context-menu">
            <ul class="navbar-nav text-center gap-2">
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">First Action</a>
                </li>
                <hr class="m-0 mx-2" />
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">Second Action</a>
                </li>
                <hr class="m-0 mx-2" />
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">Third Action</a>
                </li>
            </ul>
        </nav>

I am working on creating a custom context menu as a replacement for the browser's default context menu.

To truly experience the issue, I recommend expanding the code snippet to fullscreen mode. Scroll down slightly and observe that the context menu appears below the cursor.

Answer №1

Special thanks to @Teemu for helping me resolve the issue at hand. The problem stemmed from my usage of e.pageX and e.pageY. Instead, it is recommended to utilize e.clientX and e.clientY

e.pageX and e.pageY retrieve coordinates based on the page itself, not the screen. This causes fluctuations in the context menu position as the mouse moves. On the other hand, e.clientX and e.clientY provide steady pixel-based coordinates of the client's screen, ensuring that the context menu stays aligned with the mouse pointer.

// Script to display a customized context menu with a sliding effect
function showContextMenu(x, y) {
    var menu = document.querySelector(".ctx-context-menu");
  
    if (menu.classList.contains("ctx-menu-visible")) {
        menu.classList.add("ctx-menu-slide");
        menu.style.left = x + "px";
        menu.style.top = y + "px";
    
        setTimeout(function () {
            menu.classList.remove("ctx-menu-slide");
        }, 500);
    } else {
        menu.style.left = x + "px";
        menu.style.top = y + "px";
        menu.classList.add("ctx-menu-visible");
    }
}

function hideContextMenu() {
    var menu = document.querySelector(".ctx-context-menu");
    menu.classList.remove("ctx-menu-visible");
}

document.addEventListener("contextmenu", function (e) {
    var menu = document.getElementById("ctxContextMenu");

    if (menu.contains(e.target)) {
        return;
    }
    e.preventDefault(); 
    showContextMenu(e.clientX, e.clientY); 
});

document.addEventListener("click", function (e) {
    var menu = document.getElementById("ctxContextMenu");
    if (!menu.contains(e.target)) {
        hideContextMenu();
    }
});

document.getElementById("ctxContextMenu").addEventListener("contextmenu", function (e) {
    e.preventDefault(); 
});

document.addEventListener("keydown", function (e) {
    if (e.key === "Escape") {
        hideContextMenu();
    }
});
/* CSS for custom context menu */
.ctx-context-menu {
    opacity: 0;
    position: fixed;
    z-index: 10000;
    width: 150px;
    background-color: #1a1a1a;
    border: 2px solid #ff0000;
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
    border-radius: 10px;
    padding: 10px 0;
    transition: opacity 0.15s linear; 
    pointer-events: none; 
    
}

.ctx-menu-slide {
    transition: left 0.25s, top 0.25s; 
}

.ctx-menu-visible {
    opacity: 1;
    pointer-events: auto; 
    
}

.ctx-context-menu-item {
    color: #808080;
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s linear;
}

.ctx-context-menu-item:hover {
    color: #ff00ff;
}

/* End of custom context menu styling */
<html>
<head>
        <meta charset="utf-8" />
        ...
        // content omitted for brevity
        ...        
    </head>

  // Scrollable text area:
  <p>
  // Lorem ipsum text content
  
  </p>
  
<nav id="ctxContextMenu" class="ctx-context-menu">
            <ul class="navbar-nav text-center gap-2">
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">First Action</a>
                </li>
                <hr class="m-0 mx-2" />
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">Second Action</a>
                </li>
                <hr class="m-0 mx-2" />
                <li>
                    <a class="ctx-context-menu-item text-center mx-2">Third Action</a>
                </li>
            </ul>
        </nav>
    </html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The ValidationPipe in NestJS seems to be facing issues specifically with @Query() parameters

I've been attempting to convert certain query parameters from string to integer using the built-in NestJS ValidationPipe, but unfortunately, it doesn't appear to be functioning correctly. Below is my controller : import { ..., ValidationPipe } f ...

JavaScript Firebase: Service worker malfunctioning during navigation

I'm currently developing a website that relies on firebase messaging. To make this happen, a specialized service worker for firebase has been integrated into the site. This website functions as a webchat platform where messages are synchronized and s ...

The CSS for a VueJS compiled app seems to fail to apply properly unless manually copied and pasted into the browser's style editor

After compiling my vuejs app with npm run build, I noticed that the CSS does not display when viewing it in Firefox. Surprisingly, the styles do load in the network tab and appear under the style editor, but with "0 rules". However, everything displays fin ...

Retrieving an image from an input file and setting it as the background of a div element

I am currently developing a whack the mole game using Vanilla JS, and I'm attempting to allow players to choose their own target by uploading an image file. However, despite days of searching for a solution, I have not been successful. HTML: <inpu ...

Encountering errors while testing a NUXT.js and Vue.js application using Jest, specifically seeing messages like '[vuex] module namespace not found in mapState()' and '[vuex] unknown action type'

Despite NUXT's automatic namespacing feature, I am encountering difficulties testing or referencing the store in my testing modules. Can anyone offer advice on how to edit the namespacing property in a Nuxt app? Below is the code for the component, s ...

The JS script is activated only when the window is resized

I have incorporated a touch image slide using an external library called SwipeJS. However, it seems to only function properly when I resize my browser window. Within the body, I have structured my images in the Swipe Container as follows: <div id=&apo ...

Encountering a "self is not defined" error while utilizing the Jodti-React text editor within a Next.js project

Issue with 'self is not defined' error while using jodti-react in a Next.js project import React, { useState, useRef, useMemo } from "react"; import Dashborad from "./Dashborad"; import JoditEditor from "jodit-react" ...

Conceal any zero values from an empty numerical input

Currently, I have a form that retrieves data from a database and includes a number input type field. When the field is empty, it defaults to displaying "0." However, I would like to hide the "0" and only show the value if it is different from 0. Despite a ...

Sending authorization codes to web pages

I have developed a user authentication system that generates an access token after successful login. The challenge I am facing is passing this token to different pages as a header parameter in order to grant access to those pages. module.exports.authen ...

Prevent image element from overflowing in HTML

How can I prevent the images on the right from extending beyond the right padding? I want the fourth image to align with the red block above it. https://i.sstatic.net/KUE62.png Does that clarify things? <div style="margin: 0 15px;height: 40px; back ...

The custom color override feature in Bootstrap is not being applied as expected

I attempted to incorporate a new theme color into Bootstrap's color scheme, but encountered difficulties in getting it to work. To guide me through the process, I referred to this YouTube tutorial as well as the official Bootstrap documentation. Des ...

Verify that each interface in an array includes all of its respective fields - Angular 8

I've recently created a collection of typed interfaces, each with optional fields. I'm wondering if there is an efficient method to verify that all interfaces in the array have their fields filled. Here's the interface I'm working wit ...

Retrieving an attribute through the act of clicking a button

How can I retrieve the rel attribute value when clicking on a button with the class selector? <button class="nameClass" rel="relName">Content</button> I am attempting to achieve this by: $(".nameClass").click(function(){ // Here is where ...

Getting the Request Body Content in Express Middleware

Currently, I am in the process of developing a small API logger to use as an Express middleware. This logger is designed to gather data from both the request and response objects, then store this information in a JSON file on disk for later reference. Her ...

I kindly request your assistance in resolving the issues with the append() and empty

Here is some code I've been working on: <!DOCTYPE html> <html> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script> $(document).ready(function(){ ...

Steps for creating a CodeBlock in a Next.js Website blog similar to the one in the provided image

Learn how to insert a code block in Next.js. def greet(name): """ This function greets the person passed in as a parameter. """ print("Hello, " + name + ". Good morning!") Here is an example of ...

Exploring the functionalities of jQuery and Local Storage: Understanding the Selector's various states

I am currently developing a new feature for font customization on a website. The drop-down menu offers several font options to choose from. When a user clicks on one of these options, the following code is executed: jQuery(function($) { if (localStorage.g ...

What is the current state of Javascript in versions 3.4 and 3.5 of ANTL

Can someone provide information on the current status of the JavaScript target in ANTLR 3.4 or 3.5? I've searched online for answers but haven't found anything conclusive. While I know it was fixed in v3.3 after being broken in v3.2, there is no ...

What is the best way to execute synchronous calls in React.js?

Currently, I am a novice in working with React JS and I have been tasked with implementing a feature to reset table data in one of our UI projects. Here is the current functionality: There is a save button that saves all overrides (changes made to the or ...

The jQuery selectors are not able to identify any dynamically generated HTML input elements

After successfully injecting HTML code into the DOM using Ajax, I encountered an issue where my jQuery selector was not working for a specific HTML input element. For example, when attempting to use the following jQuery code: $("input[id*='cb_Compare ...