Javascript: add item to Context Menu - javascript

When clicking on a particular HTML element I want a custom element to show up in the context menu which on click calls a Javascript function.
// The element to whose context menu the item should be added.
const element = document.getElementById('example');
// The menu item that should be added.
const menuItem = document.getElementById('menuItem');
element.addEventListener('contextmenu', e => {
//e.menuItems.add(menuItem); // How to get it into the menu?
});
#example{border:thin solid;padding:1em;}
<p id="example">
I want to add an item to the context menu
hat opens on this paragraph.
</p>
<!--
the element below is supposed to show
in the context menu instead of inline.
-->
<span
onclick="javascript:alert('the menu item has been clicked')"
id="menuItem">
Click me!
</span>

You cannot modify the browser context menu without using a WebExtension, the best solution is to use e.preventDefault() which stops the browser context menu showing then adding your own context menu like popup.

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Context_menu_items
"permissions": ["contextMenus"]
browser.contextMenus.create(
{
id: "log-selection",
title: browser.i18n.getMessage("contextMenuItemSelectionLogger"),
contexts: ["selection"],
},
onCreated
);
browser.contextMenus.onClicked.addListener((info, tab) => {
switch (info.menuItemId) {
case "log-selection":
console.log(info.selectionText);
break;
// …
}
});

Related

document.querySelector() always selects the element with the class "Open" even after it has been removed and replaced with the class "Close" | Vue3 js

So I was trying to find a Vue Solution of the jQuery SlideUp() and slideDown() functionality. And I have run into a problem while creating the same for my Side Bar Menu Open and Close funtion. See my code below:
UPDATE: Here's the stackblitz link to the problem: https://stackblitz.com/edit/vue-lvjxmz
I'd really appreciate if someone helps me to solve this problem. Thanks.
export default {
mounted() {
let hasSubmenu = Array.from(document.querySelectorAll(".hasSubmenu.close"));//Select all hasSubmenu
let self = this;
hasSubmenu.forEach(function (el) {//Show Hide Submenu Based On Clicks
el.addEventListener("click", self.menuOpen.bind(this, el));
});
},
methods:{
//Menu Opener Function
menuOpen(el){
let ul = el.querySelector("ul");
ul.style.height = `${ul.scrollHeight}px`;
el.classList.remove("close");//Remove The Class "close"
el.classList.add("open");// add the class "open"
this.menuClose();//Call this function to close the menu
},
//Menu Closer Function
menuClose(){
console.log(document.querySelector(".hasSubmenu.open"));//Log the Selected Element
document.querySelector(".hasSubmenu.open").addEventListener("click", function (e) {
e.currentTarget.querySelector("ul").style.height = "0px";
e.currentTarget.classList.remove("open");
e.currentTarget.classList.add("close");
});
}
},
}
<ul id="main-nav">
<li class="hasSubmenu close">
<div> Main Menu One</div>
<ul style="height: 0px">
<li>
Sub Menu One
</li>
<li>
Sub Menu Three
</li>
</ul>
</li>
</ul>
So what we are doing here is: we are first selecting all the hasSubmenu items with the class close has in it. Then firing the click event where the menu opens and replace the class close with "open". Then we are calling the menuClose function where we are selecting the element which has the .open class. then firing another click event when the menu closes and removes the class .open and adds .close again.
The problem is somehow the menuClose function keeps selecting the element as if it was pointing to the hasSubmenu element and caching it in, it doesn't matter whether the class has been changed or not. I mean, even after the .open class has been removed on the click event it stills selecting it and thus clicking on the menuItem after the first 2 clicks the menuOpen function first opens the menu then calls the menuClose function and immediately closes the menu again although menuClose functionality only added to document.querySelector(".hasSubmenu.open"). As if, it first selected the element itself not the class and cached it in.
Okay, as per James Reply I have managed to solve the problem in another way. So the solution goes as below:
// Simply write one function and check if the submenu's
// height is 0px or not. Based on that write your
// open and close logics
menuOpenClose(el){
let ul = el.querySelector("ul");
let getHeight = ul.style.height;
console.dir(getHeight);
if (getHeight == '0px' || !getHeight.length) {
ul.style.height = `${ul.scrollHeight}px`;
el.classList.remove("close");
el.classList.add("open");
} else{
ul.style.height = `0px`;
el.classList.remove("open");
el.classList.add("close");
}
}

Dynamically add/hide tabpanel in viewport

This code is executed when clicking on menu item:
var objectTabPanel = Ext.create('App.view.ObjectTabPanel');
var tab = Ext.getCmp('mainTabPanel').add(objectTabPanel);
But if I click on the menu item again, the tabpanel is created again.
How to create a condition? If the tabpanel is created and opened, then switch to it. If the tabpanel is not created and is not open, then create and switch to it.
I want the behavior such as, for example, in SublimeText:
You can use Ext.getCmp('componentID').show():
...
onObjectsClick: function(item, e, eOpts) {
if(!Ext.getCmp('objectsPanel')) {
var objectsPanel = Ext.create('AMS.view.ObjectsPanel');
Ext.getCmp('mainTabPanel').add(objectsPanel);
}
Ext.getCmp('objectsPanel').show();
},
...

How to add multiple items to context menu in Kendo Scheduler?

"kendoContextMenu" is one of control from Telerik suit. I am trying to attach it with Kendo Scheduler control.
Below is the code to render scheduler and menu
Part of it taken from Kendo sample site
<div id="example">
<div id="scheduler"></div>
<ul id="contextMenu"></ul>
</div>
Here is Context Menu Initialization
$("#contextMenu").kendoContextMenu({
filter: ".k-event, .k-scheduler-table td",
target: "#scheduler",
select: function(e) {
var target = $(e.target);
if (target.hasClass("k-event")) {
var occurrenceByUid = scheduler.occurrenceByUid(target.data("uid"));
} else {
var slot = scheduler.slotByElement(target);
}
},
open: function(e) {
var menu = e.sender;
var text = $(e.target).hasClass("k-event") ? "Edit Title" : "Block";
menu.remove(".myClass");
menu.append([{text: text, cssClass: "myClass" }]);
}
});
});
The above code adds only ONE item in context menu and click event directly fires up. I would like to have multiple items in a context menu and each should have its own event so that I can use them as it clicked.
Below image shows right click behavior, where it shows only Block in a menu
I am trying to get menu as below- which has multiple items and have its own click events
I am trying like below by appending text but it's seems to be wrong way to do and it can not have separate click event.
open: function(e) {
var menu = e.sender;
var text = $(e.target).hasClass("k-event") ? "Edit event" : "Add Event";
text = text + "|" + "Cancel"
menu.remove(".myClass");
menu.append([{text: text, cssClass: "myClass" }]);
}
Kindly help
I'm afraid you're appending it wrong. By concatenating "| Cancel" you're not adding a new item, but adding text to the existing one.
Try creating a new object and append it with append():
menu.append([{text: "Cancel", cssClass: "cancel-opt" }]);
Then you check by the class inside the select event:
if (target.hasClass("cancel-opt"))

jQuery Accordion | Open first element on pageload & active state confusion

I am using the Javascript below to animate an accordion (it's a slightly modified variant of the one explained here: http://tympanus.net/codrops/2010/04/26/elegant-accordion-with-jquery-and-css3/.
Now I wanted to have the first element to be open on pageload, so I figured I just give it some sort of extra-class via Javascript (and define that .active state via CSS) to have it open up.
This worked, however if I hover over any but the first-element with said .active class, the first element keeps its state, and stays open until I hover over it at least once.
So, what I want is: the first element of my accordion is open and collapses if the user hovers over any of the elements that are not the first. I think I need to add a line in the hover function to either take the class away of the first element or to give the new element the active state, but I don't know how to do it and keep breaking the thing.
<script type="text/javascript">
jQuery(function() {
activeItem = jQuery("#accordion li:first");
jQuery(activeItem).addClass('active');
jQuery('#accordion > li, #accordion > li.heading').hover(
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
},
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
}
);
});
</script>
Looks like this is happening because each accordion item has its own hover event that takes care of its own animation. You can refactor the code slightly to make this easier to understand and reuse:
var activeItem = jQuery("#accordion li:first");
jQuery('#accordion > li, #accordion > li.heading').hover(
function () { hoverMe(jQuery(this)); },
function () { unhoverMe(jQuery(this)); }
);
//This gets called when cursor hovers over any accordion item
var hoverMe = function(jQuerythis) {
//If the first item is still active
if (activeItem) {
contract(activeItem); //...Shrink it!
activeItem = false;
}
//Expand the accordion item
expand(jQuerythis);
};
//This gets called when cursor moves out of accordion item
var unhoverMe = function(jQuerythis) {
contract(jQuerythis);
};
//I have moved the hover animation out into a separate function, so we can call it on page load
var expand = function(jQuerythis) {
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
};
//I have moved the unhover animation out into a separate function, so we can contract the first active item from hoverMe()
var contract = function() {
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
};
//Now expand the first item
expand(activeItem);
I have put together a simplified version demonstrating the logic. Please let me know how you get on.

Is there a dojo enhanced grid example with context menu

I am looking for an example of a dojo enhanced grid that contains a context menu on either a cell or row menu where the cell or row data is accessed. I have managed to create an enhanced grid with a row context menu. I can create a function that captures the event of clicking on the row menu item. However, I am not sure how to access the row data in the context of the menu item handler. I have not seen any example in the tests of the nightly build. Is there an example of this available online?
I had a similar question. I wanted to create a context menu which allowed the user to remove the item that they right clicked on from the datagrid and delete the item from the datastore. Thought it should be pretty simple and with your help and some other sites, I came up with the following code.
var selectedItem; // This has to be declared "globally" outside of any functions
function onRowContextMenuFunc(e) {
grid5_rowMenu.bindDomNode(e.grid.domNode);
selectedItem = e.grid.getItem(e.rowIndex);
}
function gridRowContextMenu_onClick(e) {
store3.deleteItem(selectedItem);
}
.
<div dojoType="dijit.Menu" id="grid5_rowMenu" jsId="grid5_rowMenu" style="display: none;">
<div dojoType="dijit.MenuItem" onClick="gridRowContextMenu_onClick">Delete</div>
<div dojoType="dijit.MenuItem">Cancel</div>
</div>
.
<div id="grid" dojoType="dojox.grid.DataGrid" jsId="grid5" store="store3" structure="layoutStructure" rowsPerPage="40" onRowContextMenu="onRowContextMenuFunc"></div>
Of course, if you were programatically creating your DataGrid, you would just add onRowContextMenu: onRowContextMenuFunc to your declaration.
I figured it out. On the row context menu even, capture the row number into a global. On a click even on the menu item, retrieve the row from the global and then use it to lookup the contents of the row in the grid. I have been using this method and it has worked perfect.
Here's how to access the selected row from the context menu:
// First create a menu object to hold the various menus
var menusObject = {
// headerMenu: new dijit.Menu(),
rowMenu: new dijit.Menu()//,
// cellMenu: new dijit.Menu(),
// selectedRegionMenu: new dijit.Menu()
};
Add a menu item
menusObject.rowMenu.addChild(new dijit.MenuItem({
label: "Show me data",
onClick: function(e){
console.log(this.selectedRow)
}
}));
menusObject.rowMenu.startup();
Create the grid
var grid = new dojox.grid.EnhancedGrid({
store : store,
structure : layout,
rowsPerPage: 10,
escapeHTMLInData: false,
plugins: {
menus: menusObject
}
}, 'some are to place');
// Activate message sending from data grid row to menu items
dojo.connect(grid, 'onRowContextMenu', function(e)
{
// Set the "selectedItem" property of all of the menu items of a menu. This lets you reference the row data!!
var menuChildren = menusObject.rowMenu.getChildren();
for(var i = 0; i<menuChildren.length; i++){
menuChildren[i].selectedRow = this.getItem(e.rowIndex);
}
});

Categories

Resources