4

I'm trying to open an anchor context menu using only JavaScript, eg for this HTML:

<html>
  <head></head>
  <body>
    <a href="https://stackoverflow.com" id="anchor-el"> Anchor </a>
  </body>
</html>

I want to open the context menu with the native 'Open in link new tab' and 'Open link in new window' options using just JavaScript.

So far I've tried this, and it seems to successfully dispatch a contextmenu event to the anchor, but the context menu doesn't actually show...

document.getElementById('anchor-el').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }))
Dan Hedgecock
  • 1,213
  • 11
  • 15
  • It looks like the top answer from this post points you in the right direction- https://stackoverflow.com/questions/7914684/trigger-right-click-using-pure-javascript?answertab=active#tab-top – wariofan1 Oct 12 '20 at 02:55

5 Answers5

1

From what I understand of your question, you wish to "replace" the normal click event result with the contextmenu result... But with just the two first items of that context menu.

That makes it a custom menu that you have to define. So here is something...

let contextElements = document.querySelectorAll(".context-anchor")
let myContext = document.querySelector(".context")
let contextItems = document.querySelectorAll(".context-item")
let contextHref

// To add event a listener on each .context-anchor element in order to show a "simulated context menu"
contextElements.forEach(function(ce){
  ce.addEventListener("click", function(e){
    e.preventDefault()
    
    // Get the click coord to open the context menu at the right place
    let clickCoords = {x: e.pageX, y: e.pageY}
    // Get the href of the clicked link
    contextHref = ce.href
    
    // Create a mouse event
    let event = document.createEvent('MouseEvents');
    event.initEvent('mycontextmenu', false, true);
    
    // Be ready to handle it
    this.addEventListener('mycontextmenu', function (e) {
      myContext.style.top = clickCoords.y
      myContext.style.left = clickCoords.x
      myContext.style.display= "block"
    }, false);
    
    // Dispatch it
    this.dispatchEvent(event);
  })
})

// Listener for the options of that "simulated context menu"
contextItems.forEach(function(ci){
  ci.addEventListener("click", function(e){
    if(this.getAttribute("data-destination") === "tab"){
      window.open(contextHref,"_blank")
    }else{
      window.open(contextHref,"custom",`width=${0.99*screen.width},height=${0.94*screen.height}`)
    }
  })
})

// To hide the "simulated context menu" when there is a click anywhere else than on a .context-anchor element
document.addEventListener("click", function(e){
  if(myContext.style.display==="block" && e.target.classList.toString().split(" ").indexOf("context-anchor")<0){
    myContext.style.display= "none"
  }
})
.context{
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  border: 1px solid lightgrey;
  background: white;
  margin: 1em;
  box-shadow: 2px 2px 2px grey;
  min-width: 15em;
}

.context-item{
  font-family: "arial";
  padding: 0.5em 2em;
}

.context-item:hover{
  background: lightgrey;
}
<a href="https://stackoverflow.com" class="context-anchor"> Anchor </a><br>
<br>
<a href="http://hmpg.net/" > Normal anchor </a>

<!-- The simulated context menu -->
<div class="context">
  <div class="context-item" data-destination="tab">Open link in a new tab</div>
  <div class="context-item" data-destination="window">Open link in a new window</div>
</div>

NOTE: window.open is blocked in SO snippets for obvious reasons. Try this CodePen for a working demo.

That definitely is a lot of code to create a weird and uncommon browser behavior. So I would not recommand anyone to use it.

I posted that because it was a bounty challenge .oO(lol!)

Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
  • 1
    This is the most helpful answer I've found so far. From what I can tell opening the native browser contextmenu isn't possible, so opening a custom contextmenu seems to be the only option. (For the curious I need this for a div, that cannot be an anchor but has an anchor inside it, that receives click events. I want to "forward" all click events on the div to the anchor inside it and can't figure out how to "forward" the right click event. Kind of like this but also works for right clicks: https://eng.wealthfront.com/2020/10/01/building-a-truly-accessible-clickable-div/ – Dan Hedgecock Oct 18 '20 at 03:49
  • Thanks for that nice comment ;) --- Now, `opening the native browser contextmenu isn't possible` - is TRUE *(from what I know)*. --- About "forwarding" events... You mean to handle an event made on a `parent` and use the handler of it's `child`? Nahhh... I am not sure why one would like to do it. It is easy to imagine that there will be more than one `child` that will try to handle that "forwarded event". I doesn't look like a good idea. ;) – Louys Patrice Bessette Oct 20 '20 at 19:47
0
document.getElementById("anchor-el").addEventListener("click", function() {
    var link = document.getElementById('anchor-el').getAttribute("href");
    window.open(link,'_blank');
});

I believe it'll help you.

0

If i understood correctly you have to create a custom contextmenu. So here is an example.

const menu = document.querySelector('[data-id=anchor-el]')
const anchor = document.getElementById('anchor-el');

anchor.addEventListener('contextmenu', e => {
  e.preventDefault();
  menu.style.top = e.pageX;
  menu.style.top = e.pageY;
  menu.style.display = 'block';
});

menu.querySelector('li#newTab').addEventListener('click', (evt) => {
  evt.preventDefault();
  console.log('clicked open in new tab');
  window.open(anchor.href);
});
menu.querySelector('li#newWin').addEventListener('click', (evt) => {
  evt.preventDefault();
  console.log('clicked open in new window');
  window.open(anchor.href, '_blank', 'toolbar=0,location=0,menubar=0');
});

document.body.addEventListener('click', (evt) => {
  evt.preventDefault();
  evt.stopPropagation();
  menu.style.display = 'none';
});
[data-id="anchor-el"] {
  width: 15rem;
  display: flex;
  margin: 0;
  padding: 0;
  align-items: stretch;
  align-content: space-evenly;
  flex-direction: column;
  justify-content: space-evenly;
  box-shadow: 0 0.25rem 0.325rem 0.175rem rgba(0, 0, 0, 0.2);
  position: relative;
  display: none;
}

[data-id="anchor-el"] ul li {
  width: 100%;
  list-style: none;
  margin: 0;
  padding: 0.5rem;
  position: relative;
  color: #000;
  font-weight: 500;
  font-size: 1rem;
  cursor: pointer;
}

[data-id="anchor-el"] ul li:hover {
  color: #f00;
}
<a href="https://stackoverflow.com" id="anchor-el"> Anchor </a>

<div>

  <div type="context" data-id="anchor-el">
    <ul>
      <li label="Open in new tab" id="newTab">Open in new tab</li>
      <li label="Open in new window" id="newWin">Open in new window</li>
    </ul>
  </div>

</div>
0

If you want to have Open In New Tab and Open In New Window options in the menu, why just use HTML?

<html>
  <head></head>
  <body>
    <a href="https://stackoverflow.com" id="anchor-el" target="_blank" title="If you run this snippet and click on this link, it won't work because you need to run it in your own editor"> Anchor </a>
  </body>
</html>

The above code will open in a new tab whenever clicked. Also there will be an option for Opening in a new Tab and Opening in a new window, when it is right clicked.

The attribute "target" specifies where the link will be opened; Below are some examples:

target="_blank" - "Opens in new tab"
target="_self" - "Opens in the same tab"
target="_parent" - "Opens in the parent frame"
target="_top" - "Opens in the full body of the window"
-1

Opening native system context menus, e. g. the default right click context menu is not possible.
You can, of cause create your own context menu using jQuery for example:
https://swisnl.github.io/jQuery-contextMenu/

As an example for how to use the library:

 $(function() {
        $.contextMenu({
            selector: '#example', 
            trigger: 'left',
            callback: function(key, options) {
                var m = "clicked: " + key;
                window.console && console.log(m) || alert(m); 
            },
            items: {
                "edit": {name: "Edit", icon: "edit"},
                "cut": {name: "Cut", icon: "cut"},
               copy: {name: "Copy", icon: "copy"},
                "paste": {name: "Paste", icon: "paste"},
                "delete": {name: "Delete", icon: "delete"},
                "sep1": "---------",
                "quit": {name: "Quit", icon: function(){
                    return 'context-menu-icon context-menu-icon-quit';
                }}
            }
        });
        
        /* prevent the default switching to the target site */
        $("#example").on("click", function(event) { 
            event.preventDefault(); 
        });
    });
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.ui.position.js"></script>

<a href="https://google.com" id="example">click me</a>
WolverinDEV
  • 1,494
  • 9
  • 19