function openContextMenu(e, items) { e.preventDefault(); e.stopPropagation(); closeContextMenu(); var overlay = document.createElement('div'); overlay.className = 'cm-overlay'; overlay.id = '_ctxMenuOverlay'; overlay.onclick = function() { closeContextMenu(); }; overlay.addEventListener('contextmenu', function(ev) { ev.preventDefault(); ev.stopPropagation(); var cx = ev.clientX, cy = ev.clientY; closeContextMenu(); var el = document.elementFromPoint(cx, cy); if (el) el.dispatchEvent(new MouseEvent('contextmenu', {bubbles:true, cancelable:true, clientX:cx, clientY:cy, button:2})); }); var menu = document.createElement('div'); menu.className = 'cm-menu'; menu.id = '_ctxMenu'; menu.style.left = e.clientX + 'px'; menu.style.top = e.clientY + 'px'; menu.oncontextmenu = function(ev) { ev.preventDefault(); }; function addBtn(label, cls, handler) { var btn = document.createElement('button'); btn.textContent = label; if (cls) btn.className = cls; btn.onclick = function() { closeContextMenu(); handler(); }; menu.appendChild(btn); } for (var i = 0; i < items.length; i++) { addBtn(items[i].label, items[i].cls || '', items[i].handler); } document.body.appendChild(overlay); document.body.appendChild(menu); } function closeContextMenu() { var o = document.getElementById('_ctxMenuOverlay'); var m = document.getElementById('_ctxMenu'); if (o) o.remove(); if (m) m.remove(); }