Determining whether `contextmenu` command actually opened a context menu - contextmenu

I have a webextension which has a listener for contextmenu events
https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event
This event is fired when the user tries to open a context menu (usually by right-clicking) on some element. But for the event to be fired, it is not necessary that any (dom-based) context menu actually opens (you'll usually see some context menu, but it may be application/system level and not related to the dom).
Is there any way to determine programmatically whether a context menu actually opened?

We can utilize the way events are dispatched, which happens in two phases: capturing (the event "descends" the DOM tree from window to the target element) and bubbling (the event "ascends" back up to window).
So the menu will be shown if the event wasn't canceled when it has "bubbled" back to window.
window.addEventListener('contextmenu', e => {
if (e.isTrusted && !e.defaultPrevented) {
console.log('will show the menu now');
}
});
Note that the dispatch process may be stopped by some other script via event.stopPropagation() so the event will never be dispatched on window in the bubbling phase. If you want to detect this, you can listen to the event in the capturing phase and start a timer:
let timer;
window.addEventListener('contextmenu',
e => { if (e.isTrusted) timer = setTimeout(detectCanceledMenu, 0, e.target); },
true);
window.addEventListener('contextmenu', () => clearTimeout(timer));
function detectCanceledMenu(el) {
console.log('the menu was canceled');
}

Related

Have Stimulus wait for Dom changes using MutationObserver custom event listener before continuing

I am writing a Stimulus controller that has a function which needs wot wait for DOM changes before it can continue (As a Turbo Stream response needs to update a frame). Before the DOM change, it can't find the target, but it should be able to AFTER the DOM change.
I have successfully enabled MutationObserver, which is able to tell me that the child element was added--but this happens after the function/action is looking for it.
Therefore I found this article on how to use Mutation Observer to add a custom Event Listener.
I've gotten it to work as far as until the last step, where we code the Promise.
When I run the code, I get
undefined is not an object (evaluating "el.innerText.includes(text)
I tried changing it to
document.getElementByTagName(el).innerText.includes(text)
But that returned that it's not a function.
The instantiation of the MutationObserver is inside my connect() method:
connect() {
const observer = new MutationObserver( list => {
const evt = new CustomEvent('dom-changed', {detail: list});
document.body.dispatchEvent(evt)
});
observer.observe(document.body, {attributes: true, childList: true, subtree: true});
}
the waitforText(el, text, maxWait=5000) function is a controller action currently.
I then run this.waitForText("h1", "Settings for") inside the function I need it to wait. I'm merely using a h1 for testing before I put it on an element that is within the same frame as that h1.

webglcontextrestored event not firing after context lost

I'm writing a webgl application and am trying to recover a webgl context after it has been lost, but the context restored event does not seem to be firing.
I'm running the following code, and expecting both "context lost" and "context restored" to be logged, based on the WebGL specification and the documentation on the WebGL wiki. When I run the code below in jsfiddle in both Chrome (50.0.2661.102 m) and Firefox (46.0.1) I see "context lost" logged but not "context restored", and I'm seeing the same behaviour in my electron application.
var canvas = document.createElement( 'canvas' )
var gl = canvas.getContext("webgl");
var WEBGL_lose_context = gl.getExtension('WEBGL_lose_context');
canvas.addEventListener("webglcontextlost", function(e) {
log("context lost");
e.preventDefault();
}, false);
canvas.addEventListener("webglcontextrestored", function() {
log("context restored");
}, false);
WEBGL_lose_context.loseContext();
function log(msg) {
var div = document.createElement("pre");
div.appendChild(document.createTextNode(msg));
document.body.appendChild(div);
}
Do I need to do anything extra to get the context restored event to fire? Have I misunderstood the WebGL specification?
In the case of using WEBGL_lose_context you have to call WEBGL_lose_context.restoreContext(). WEBGL_lose_context is for testing only. In the normal case it's up to the browser to decide when to restore the context. For example if you're tab is not the front tab and another tab needs all the WebGL memory your tab might get a lost context event. Later when you're tab is made the active tab you'll finally get a restore context event.
See conformance test here

jQuery UI dialog binding keydown doesn't always work

I'm writing my own ESC handler because I need to do other actions when ESC is pressed, specifically I need to manage where focus goes for keyboard-only users. I have it working for all menus and some dialogs (both of which are using jQueryUI) but I'm having problems with dialogs that open on top of other dialogs (confirmation dialogs).
I'm using a Backbone View and adding my keydown handler on dialogcreate. this.$el.on('dialogcreate', this.bindKeydownEvent);
My handler:
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog().on('keydown', function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}
I've checked and this.$el.dialog() is the correct dialog when the second dialog calls this.bindKeydownEvent but for some reason the keydown handler is not being triggered no matter what I press in the dialog (Tab, Space, Enter, random letters, etc).
Any idea what I'm doing wrong or have a better way I could bind the keydown event?
EDIT:
I just noticed that this is also happening in some first-level dialogs. It looks like the only difference is the way we get the template and therefore create the interior of the dialog. In our Alert and Confirmation dialog classes, we define the template as an attribute on the object like this: template: _.template("<div><%= o.content %></div>"). In other views (in which the keydown binding works) we build the child elements and add them to the DOM of the dialog, set the template in the initialize function
this.options.template = 'navigation/CreateNewDialog.template';
or set it when we call the dialog
var closeConv = new views.CloseConversationDialogView({
confirm: this.closeConversationConfirmed,
content: i18n.t("closeConversationInput"),
template: "conversation/CloseConversationDialog.template"
});
closeConv.render();
Is there a reason that creating the template inline as an attribute on the view would not bind keydown correctly?
To understand why your event handler isn't being triggered you need first understand how event delegation works.
The key to event delegation in that events bubble up the DOM. So when you bind your event using this.$el.dialog().on('keydown',..., what you basically doing is listening to any keydown event that is triggered on your $el or it's descendants. In this case being that your second dialog isn't a descendant of your $el it's events won't bubble up to it and therefore don't trigger your handler.
To work around this you can either bind directly to your second dialog, or instead bind to a exisitng higher level element like the document. For example
$(document).on('keydown', '.myDialog', function() {...
The only thing my original attempt was missing was "widget". The widget method, according to api.jqueryui.com,
Returns a jQuery object containing the generated wrapper.
I don't see any documentation on what exactly $('.selector').dialog() returns but apparently it is not the same as $('.selector').dialog("widget"). I also changed on('keydown'... to just use the jQuery keydown instead.
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog("widget").keydown(function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}

JQuery mobile - click event only fires on current page

I have the following:
$(document).on("pageinit", function (event) {
alert("pageinit called");
$('#logout').bind('click', function() {alert("clicked!");});
});
The first time the page runs you get a single alert 'pageinit called'. Clicking the element with id #logout fires the alert 'clicked!'. If I click any other links in this page I still get the 'pageinit called' alert (and I get it multiple times, apparently for each page I have previously navigated as well) but subsequently the handler for #logout is gone and never never re-established.
Can anyone tell me how I can get the handler for #logout to remain? I've tried:
$('#logout').die('click').live('click', function() {alert("clicked!");});
to no avail.
After looking more closely (and as commented by Omar), this problem is caused by a combination of the jquery mobile paging system AND trying to attach to a 'single' element by id.
In my case each time I clicked a link within the page it would load into the jqm paging system a separate page, each one containing its own #logout element. My solution was to query for all the buttons and attach handlers to each one:
var buttons = $("*[id='logout']");
buttons.each(function() {
// handle click or whatever here
});
Instead of:
var button = $('#logout'); // Only hooks into the first #logout element

How to close a sidebar in firefox

I have a sidebar inside my firefox addon. I want the following behavior for this sidebar - I should force close the sidebar if it is open when the browser is being closed (so that the next time the browser is opened the sidebar is not in an open state). I am trying to do this:
uninit: function() {
var sidebarWindow = document.getElementById("sidebar").contentWindow;
if (sidebarWindow.location.href == "chrome://myaddon/content/mysidebar.xul") {
// Act on the sidebar content
toggleSidebar('mySampleSidebar');
}
}
I call this uninit for the window.unload event:
window.addEventListener("unload", function() { myobj.uninit()}, false);
Can someone tell me how to achieve this, as what I am trying to do is not working.
Thanks
Kapil
In your firefox sidebar overlay javascript add
toggleSidebar();
in the "load" event listener function.
See here for example:
sidebar.onFirefoxLoad = function(event) {
document.getElementById("contentAreaContextMenu")
.addEventListener("popupshowing", function (e)
{ sidebar.showFirefoxContextMenu(e); }, false);
toggleSidebar();
};
window.addEventListener("load", sidebar.onFirefoxLoad, false);
Your code is correct for closing your sidebar, but I think unload is too late to change the startup state of the browser window (browser.xul), because browser.xul has already been unloaded (and its state, including sidebar state, has already been stored away).
Instead use beforeunload. I tested the following and it seems to work fine:
window.addEventListener("unload", myobj.uninit, false)
On rare occasions the browser process could be killed so unload would not be called (user kills it or it crashes). I'm not sure if occasionally stores the state of the sidebar like it does tabs, but if it does it could open and have the sidebar visible in that rare case. To handle that case, you can add what #Vinothkumar suggested.
window.addEventListener("load", myobj.uninit, false)

Resources