Should all underlying elements be disabled when a jQuery dialog is showing? - javascript

I have a web page with a dialog I'm showing with jQuery.dialog (jQuery 1.7.1, jQuery-UI 1.8.18). I create it like this:
$(theDialog).dialog({
autoOpen: false,
width: "800px",
modal: true, // etc....
});
On the same page, I have a list control I've constructed out of a stack of <div> elements. I listen to clicks on the stacked <div> elements thusly:
$("#listEmployees div").on("click", "input", listEmployees_ItemClicked);
where listEmployes_ItemChecked is my event handler. When I show the jQuery dialog, most of the elements on the HTML page are disabled; i.e. they are unresponsive to mouse clicks &c. However, my stack of lists is not: if I click on the items in it, the event handler gets called.
To work around this issue, I had to introduce a global variable on the page, isDialogVisible which my dialog sets when it opens and clears when it closes. The event handler then ignores the event if the global variable is true. Needless to say, this is a hack and won't scale well.
My expectation was that the jQuery dialog, being modal, would disable all HTML elements and events from these elements while it is open. Is this expectation incorrect? Why am I still seeing mouse clicks when the modal dialog is up?

jQuery UI dialogs (earlier versions than 1.10) have a zIndex property. Set the zIndex to a value higher than any z-index being used within your CSS.
eg.
$(theDialog).dialog({
autoOpen: false,
width: "800px",
modal: true,
zIndex: 99
});
This feature was removed as of jQuery UI v1.10:
Removed zIndex option
Similar to the stack option, the zIndex option is unnecessary with a
proper stacking implementation. The z-index is defined in CSS and
stacking is now controlled by ensuring the focused dialog is the last
"stacking" element in its parent.
jQuery UI dialogs do have the method moveToTop(), but this only moves the dialog to the top of the dialog context, so it is above any other dialogs on the page. It's the developers job to ensure their CSS doesn't cause stacking issues. 'Unfortunately', jQuery UI doesn't hold our hand all the time.

Related

Click event not working correctly on Kendo Multi-Select wrapper (Only in FireFox)

I am having an odd problem with kendo multi-selects in FireFox.
I wrap a multiselect in a div, then attach a click event to that div wrapper. So, when the user clicks on the multiselect (the div wrapper), the click event fires, and the multiselect is enabled (initially disabled). This works fine in Chrome. But in FireFox, the click event is only fired when you click off to the right of the div wrapper, like in my picture I attached. In Chrome, if you click anywhere on the multiselect, the click event fires correctly...
This Dojo shows exactly what is happening. That dojo does the same thin. You can see it works fine in Chrome, but FireFox doesn't fire the click event unless you click on the right side of the multiselect...
Tyler:
Try this approach.
Standard JavaScript eventing is bubble up, which means an event propagates from the innermost to the outermost elements. You can force the reverse by installing a custom event listener with the useCapture argument set to true. See addEventListener.
The reverse means you can cause your wrapper to experience the click event first. The event handler for the click would enable the kendo multiselect and remove itself from listening. These actions would be equivalent to what you want to do.
// function that turns on multiselect and then removes itself from listening
function multiselectEnabler() {
$('#multiSelector').data('kendoMultiSelect').enable(true);
$('#multiSelectWrapper')[0].removeEventListener( 'click', multiselectEnabler );
}
$('#multiSelectWrapper')[0].addEventListener( 'click', multiselectEnabler, true);
/* Your original code that is no longer needed
$('#multiSelectWrapper').on('click', function(){
kendoConsole.log('Activating MultiSelect');
$('#multiSelector').data('kendoMultiSelect').enable(true);
});
$('#multiSelectWrapper input.k-input').on('click', function(){
kendoConsole.log('Clicking on multiSelect');
});
*/
I finally found a solution to this problem. It can be found In this dojo. I discovered most browsers will propagate events up through the DOM tree on disabled elements, but Firefox will not. So, there was no way of setting a click event to anything in the multi-select, or on a wrapper, etc... that would have worked.
To get this to work, you add an extra div in the wrapper, like in the dojo. You set the wrapper to position: relative and the extra div contained in your wrapper (that also contains the multi-select) to position:absolute; left: 0; right: 0; top: 0; bottom: 0; then you show and hide that extra div as you 'disable' and 'enable' the multi-select (or whatever disabled element you want to enable).
This Firefox behavior, and the solution I just mentioned, is also discussed in this Stackoverflow answer, with a working example in the dojo in my first link.

JavaScript - simulate click on contextmenu

I am trying to create web automation for a site. I am simulating clicks. Usually I add an ID to an element like below and trigger a click using the Chrome developer console and it always works.
p.s: below question is outdated.I am actually trying to click an item from context menu in web.whatsapp.com
<div id="myapp">button1</div>
<script>
$("#myapp").click();
</script>
Or, if that won't work, I use a mousedown event using pure JavaScript like below and it will work smoothly.
function triggerMouseEvent(node, eventType) {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent(eventType, true, true);
node.dispatchEvent(clickEvent);
}
var targetNode = document.querySelector("#myapp");
triggerMouseEvent(targetNode, "mousedown");
Now my question is: this is not working for the menu that is generated via the jQuery contextMenu plugin.
You can see the demo for contextMenu here, where I am testing the above via the developer console.
So when you right click on the context menu there is a list of menu items listed with ul and li tags.I manually added ID #myapp to one of menu and with below code it is also choosing correct element via developer console.
var targetNode = document.querySelector("#myapp");
targetNode;
But I am unable to trigger a click on that menu item generated via context menu. I was struggling with it for more than 24 hours. I tried all events in JavaScript that I could think of, like mouseup, mousedown, and mouseenter.
You can only click the menu items when the context menu is created, which is done dynamically by the plugin. So, we first need to trigger the menu.
Triggering a native context menu is not possible, as far as I know. Triggering a right click however is possible, you need to use the contextmenu event.
In particular, using $(el).trigger('contextmenu') seems to work fine. This seems to be working because the plugin explicitly supports this. When that is done, we need to trigger a click on the menu item we want to click on.
// first, trigger the context menu
$('.button-that-has-contextmenu').trigger('contextmenu');
// then click the menu item you want to click
// substitute the correct index in `eq(X)` here
$('.context-menu-item:eq(1)').trigger('mouseup');
Because this plugin is not hosted on a CDN, it is not easy to create a snippet here. I have however created a JSFiddle to demo this fully, where I copy + pasted the plugin source into the fiddle.
In the comments, you asked about web.whatsapp.com. I happen to have a WhatsApp account and tested it there. It seems that they are not using the jQuery contextMenu plugin. A $ function is indeed defined, but it appears not to be jQuery, but just a shortcut to document.querySelector.
What their custom context menu code is I don't know, but I did manage to trigger it. Basically, I used the vanilla JS version of the above code and then had to figure out which element to trigger. The latter was easier than I thought. There is a list of chats that is open, .infinite-list-item elements. Their order in the DOM does not match the visual order, but finding the one you want can be done using the browser inspector for example. Given that you have that, you want the .chat element inside of that list item. Say we are interested in the second item, we can select it as follows:
var target = $('.infinite-list-item:nth-child(2) .chat');
You can check in the browser console that you got the right element. Then, fire an event as follows.
var evt = new MouseEvent('contextmenu', {
bubbles: true,
cancelable: true,
view: window,
buttons: 2
});
target.dispatchEvent(evt);
This uses the modern MouseEvent constructor, code is based on this example.
The above will open the context menu in the upper left corner of your browser. You can change the position by passing a clientX and clientY property into the MouseEvent constructor, that represent a position in the window.
When that is done, you can click the items in the context menu. Except you cannot automate this using JavaScript. I peeked at the source and the click handler of those menu items checks if event.isTrusted, meaning that you cannot programmatically trigger a click on them.
You may want to look into using something that can emulate clicks on a lower level, like Selenium 2.0 (WebDriver), or some wrapper around it like WebdriverIO.

ExtJS change mask target while opening an Ext.Window

I have an ExtJS application and I want to change the mask target while showing an Ext.Window. Now when I show the window, the entire DOM is masked, but I want to mask just a DIV. Overriding getMaskTarget is not working, it gets the target through an Ext.panel.Header.
Maybe you can setup you window as modal: false and mask any component / element in window show event handler (remove mask in hide or destroy event handler depending on your closeAction)?

best practice for opening and/or initializing jquery ui dialog

There are two ways to open a jquery dialog:
1) Set the autoOpen=true when initializing a dialog
2) After initialization (with autoOpen = false), call $("#id").dialog("open");
At the moment, in my code, whenever a user clicks a button, a modal dialog opens by using the first method. On "Ok" or "Cancel" the dialog is closed $(this).dialog("close");
Is it bad that on every click the dialog is opened using autoOpen=true ? In other words, is it bad that the dialog is reinitialized on every click ?
From a performance, point of view, will reinitializing the dialog on every click cause problems such as dialog events being registered multiple times ?
What is the jquery best practice for this ?
Code:
$("#button").on("click",function(){
$("#dialogHello").dialog({
modal: true,
autoOpen: true,
buttons: [
{ "text" : "Ok", "click" : function() { $(this).dialog("close"); } },
{ "text" : "Cancel", "click" : function(){ $(this).dialog("close"); } }
]
});
});
By observing the DOM, I noticed that the html elements that jquery ui dialog injects in the DOM are not duplicated if the jquery ui dialog is re-initialized on every call. If the jquery ui dialog DOM elements already exists then they are simply reused.
The following thread :
jQuery draggable - what happens if it is applied twice to an element?
Explains that the jquery events won't be registered several times on the same element if it already exists. For example, in my scenario, if I re-initialize the jquery ui dialog on every call then there won't be incrementing events being registered.
I generally initialize my dialogs once with autoOpen set to false, and then 'open' them whenever they are needed. I'll possibly update the dialog content before opening if needed.
If for some reason you do feel the need to re-init it each time, then you should at least use the 'destroy' method first to avoid causing problems and bloat.

JavaScript/jQuery: global variable inexplicably being reset, causing menu contraction issue

http://jsfiddle.net/VhjR7/1/
When you click the my lists menu once, it expands, but if you click it again, it doesn't contract.
The problem is listsExpanded being inexplicably reset to false after it is set properly to true by listsExpand(). This causes the check within $('#mid-wrap').delegate() to inappropriately call listsExpand() again, instead of listsContract() like it should.
I can't figure out where or why this reset is occurring, but I think it has something to do with the sticky light blue menu functionality. Before I started removing and replacing this blue bar after scrolling to fix an IE7 bug, there was no issue with expansion/contraction of the little white menu.
Any ideas on what's causing this?
The issue is that the hover event does not support both function arguments (in and out) when used with .delegate(). You will need to use mouseenter and mouseleave instead of hover.
Change to this:
$('#mid-wrap').delegate('#lists', 'mouseenter', function() {
listsMouseIn = true;
}).delegate('#lists', 'mouseleave', function() {
listsMouseIn = false;
});
FYI, if these HTML objects are static, not added dynamically, you could significantly simplify your code by using direct event handlers on that actual objects rather than .delegate and just stopPropagation() when you've processed the click. Then, you'd see the click first in the object and wouldn't be processing the same click multiple times causing you to need all these global flags to keep track of state.
You could also just use the visibility of the object as your detection mechanism for whether the menu is open/closed too rather than a global variable.
The first part of your hover handler (with listsMouseIn = true;) never actually fires so whenever you click, your $('body').mouseUp() handler assumes that you are not hovering the lists button, and therefore hides the menu just for the $('#mid-wrap').delegate(...) handler to show it again milliseconds later.
Replacing
$('#mid-wrap').delegate('ul#lists', 'hover', funcIn, funcOut);
with
$('#mid-wrap').delegate('ul#lists', 'mouseover', funcIn).
delegate('ul#lists', 'mouseout', funcOut);
seems to do the trick.

Categories

Resources