So, I have web page that opens another page (browser tab) as a child to the first. The parent tab holds the code that performs the bulk of the application's work. The child tab holds a component that is normally embedded in the parent tab, but sometimes the user may want to open it in its own tab. So they can.
The problem is, the component in the child tab supports about 20 different keyboard commands and a number of mouse related commands (as combos with keyboard commands). I was trying to avoid remapping each keyboard command in the child window to a call in the parent window. Right now, in the parent window, I use a plugin to map and handle keyboard commands that all get sent to the right component API call.
What I want to do, is to just pipe keyboard and mouse events from the child window back to the parent, so that the regular handling of those events can proceed as normal. I don't want to have to setup specific handlers for every command all over again in the child window. Piping the key/mouse events feels more elegant, and would, ideally, take less code.
The first thing I tried, is this:
// CHILD WINDOW
window.document.addEventListener("keydown", Redispatch, false);
function Redispatch(event){
window.opener.myRedirector.dispatchChildEvent(event);
}
And in the parent window, it had this:
myRedirector.dispatchChildEvent = function(event){
var r = dispatchEvent(e);
console.log('dispatched event from child window');
}
In this case, nothing happens. The console.log() function in the parent window never fires, and stepping through it, the console.log() line never gets hit. The dispatchEvent(e) call behaves as though an error occurs, but nothing is thrown (this is the latest Chrome).
So I tried instantiating a new event in the parent like this (seen from another StackOverflow post):
myRedirector.dispatchChildEvent = function(e){
var event = document.createEvent('KeyboardEvent');
var method = typeof event.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
event[method](
/* type */ e.type,
/* bubbles */ true,
/* cancelable */ false,
/* view */ window,
/* keyIdentifier*/ e.keyIdentifier,
/* keyLocation */ e.location,
/* ctrlKey */ e.ctrlKey,
/* altKey */ e.altKey,
/* shiftKey */ e.shiftKey,
/* metaKey */ e.metaKey,
/* altGraphKey */ false
);
}
The problem here is that this doesn't initialize all the properties of the event, like keyCode and which. The keyboard handler plugin I am using doesn't read keyIdentifier, it uses keyCode. I can't manually set keyCode cause it's readonly on the object. I thought of changing the plugin to read the keyIdentifier, but that value isn't an integer, it's a Unicode string like "U+005", etc, and I don't want to remap all the keyCodes to those values in the plugin (though this may be Chrome specific).
So I also tried instantiating a CustomEvent, and just adding properties to it, which almost worked. Except CustomEvent has a type property that is set to empty string, and can't be overridden, so the plugin doesn't catch it since it's not of type 'keydown', etc.
I keep feeling like I'm missing something that should make this possible. That's why I've avoided other solutions like remapping values, or setting up all new handlers in the child window, etc.
It feels like it should be possible to just pipe keyboard/mouse events to a parent window. So far, though, I can't seem to find a way.
Any suggestions, or solutions?
So, I was using document.createEvent() and that was producing an event object with too many properties already added (that were read-only). So when I did this:
var event = new Event(childEvent.type);
This created an event of the right type, but with no keyCode or keyIdentifier, or other such properties, and I could just add them dynamically from the childEvent. Then, dispatching them worked fine.
Related
I want to add an event listener to the windowObj that on keydown, calls a function. I can not get this to work on the window object; however, I can get it working after a child of the window object (a button for example), has been clicked. I've also tried clicking on the window area around the button, thinking that maybe the window needed to be active, but this did not work. Oddly enough, this test worked when I changed "keydown" to "click".
The way I want it to work:
When the ScriptUI window displays, on keydown, a function is called.
Below is code of a simplified example of what I want to happen:
#target Photoshop
var w = new Window ("dialog");
var buttonPositions = w.add ("group");
var top = buttonPositions.add ("button", undefined, "Button");
w.addEventListener ("keydown", function (k) {handle(k)});
function handle (k) {
alert (k.keyName);
}
w.show ();
Displays when script runs
Alert box with key name displays on keydown
tl;dr: Set the active property of any control that accepts keystrokes and is a descendent in the registered element's hierarchy to true:
btn.active = true;
win.addEventListener("keydown", function (e) { alert(e.keyName); });
The Window object isn't designed to detect keydown events. This can be demonstrated by intermingling panel, statictext, and group elements with controls such as radiobutton, button, and checkbox. Pressing the tab key skips any elements that ignore keydown events, and sets the focus to the first control in line that accepts keydown events. The first control residing in the listener's hierarchy that receives focus will trigger your callback on the next keypress.
Per the Photoshop Scripting Reference (emphasis mine):
An active control is the one with keyboard focus - that is, the one that accepts keystrokes, or in the case of a Button, is selected when the user types Return or Enter in Windows, or the space bar in Mac OS.
Keydown events can propagate through a Window (or Panel, or Group element, for that matter) as part of the event registration and capture phase, but to trigger a keydown event, the actual target needs to accept that type of event.
function showDialog() {
var win = new Window("dialog");
var btn = win.add("button", undefined, "Cancel");
// Sets initial focus to a control that accepts `ev: KeyboardEvent`,
// and is a descendent in the registered `this: Window` hierarchy.
btn.active = true;
win.addEventListener("keydown", function (e) { alert(e.keyName); });
return win.show() ? 0 : 1;
}
More info at: event callbacks/listeners and control objects.
The Event Object is different when I do:
$('section#top10 ul#periodes li').click(function (e) {
and when I do:
$('section#top10 ul#periodes li').trigger('click') {
I know the trigger function triggers the first function, but the "e" argument is not the same with the two methods. With the first method I can access the srcElement, but with trigger the object attributes are not the same. I would like to know why the two arguments are different when the same function is called.
Actually when a user clicks on an element, the event carries mouse position like pageX/pageY and clientX/clientY but using trigger to fire the event programmatically, there will be no real mouse positions because the click event fired without the mouse interaction, this is one big difference, at least I can think of. That's why the e/event object is different in both cases and it makes sense, IMO.
Update :
In other words, it's not a real click event but a simulation of that event and in this case it's a tailored peace, for example, this is the prototype of the initMouseEvent (with example and on MDN), which used with createEvent
object.initMouseEvent (eventName, bubbles, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
This used to simulate a mouse event in vanilla JavaScript (it may varies in different browsers), also look at this, so in case of jQuery it's (event object created by trigger) a tailored object that jQuery build for cross browser support when using trigger and it's not same as the one that is in real click event, completely two different objects.
I have a silly (and hopefully easily fixed) problem, which I will now attempt to describe.
The scenario-> I am trying to create a context menu using HTML / CSS / JS. Just a DIV with a high z-order that appears where a user right-clicks. Simple, and that portion works. The portion which does not is my attempt to make the menu disappear if the user clicks somewhere where a context menu is not supported; I am attempting to achieve this end with a general function in the BODY tag that fires onclick. Since the BODY tag is given a z-order of -1, and any other tags which might trigger the context menu to appear are given a higher z-order value, my hope was that if I right-clicked an element with a z-order of, say, 3, then it would fire the showMenu() function; instead, it appears that it does this, as well as passes the event to the underlying BODY tag, which causes the menu to become hidden again.
As you might imagine, it is incredibly frustrating. Does anyone know how to make prevent events from being passed down? (The INPUT button is what you may want to look at, the A anchor is something similar, but not coded to work just yet).
Here's the HTML code:
http://pastebin.com/YeTxdHYq
And here's my CSS file:
http://pastebin.com/5hNjF99p
This appears to be a problem with IE, Firefox, and Chrome.
A lot of DOM events "bubble" from the bottom object up through container objects, which means they'll eventually reach the body. But you can stop this - try adding the following code to the click handler on your element:
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
...where e is the variable you already have in your function representing the event object.
event.stopPropagation(); should work in modern browsers, but the old IE way was event.cancelBubble = true; - to be safe you can just do both (but as shown above check that .stopPropagation is defined before trying to call it).
With the above code added, if you click on the element your function will stop container objects (include the body) from seeing the click. If you click somewhere else your function isn't called so then the body will process the click.
There's more info about this at MDN and QuirksMode.org.
Note: I've ignored the z-order issue because in this case I think it is a non-issue - all elements are descendents of the body so (unless you stop it) I would expect events to bubble to the body regardless of z-order.
Is it possible to determine whether a user is active on the current web page or, say, focused on a different tab or window?
It seems that if you switch tabs, any JavaScript set on a timeout/interval continues running. It would be nice to be able to 'pause' the events when the user is not on the page.
Would something like attaching a mouseover event to the body work, or would that be too resource-intensive?
You can place onfocus/onblur events on the window.
There's wide support for those events on the window.
Example: http://jsfiddle.net/xaTt4/
window.onfocus = function() {
// do something when this window object gets focus.
};
window.onblur = function() {
// do something when this window object loses focus.
};
Open Web Analytics (and perhaps some other tracking tools) has action tracking
You could keep an alive variable going using mousemove events (assuming the user does not leave the mouse still on the page). When this variable (a timestamp likely) has not been updated in x seconds, you could say the page is not active and pause any script.
As long as you do not do a lot of processing in the body event handler you should be okay. It should just update the variable, and then have a script poll it at a certain interval to do the processing/checks (say every 1000ms).
Attach listeners to mousemove, keyup and scroll to the document.
I use this throttle/debounce function (which works without jQuery, even though it's a jQuery plugin if jQuery is present) to only run code in response to them once in ~250ms, so that you're not firing some code on every pixel of the mouse moving.
You can also use the visibilityState of the document:
document.addEventListener("visibilitychange", function() {
if( document.visibilityState === 'visible' ) {
// Do your thing
}
});
There is a wide acceptance of this API.
I've been trying to create a game in strictly HTML5 and JavaScript and have run into an issue that I can't seem to wrap my head around. In an attempt to try and avoid using third party classes/libraries, I'm creating my own class for handling custom buttons within the HTML5 canvas element. I almost got them to work and then had to re-write most of the script after realizing that my event listeners kept adding on top of each other every time the canvas redrew (I was using an anonymous function in the mouseDown event listener before, but have since switched to a different method).
First of all, my event listeners now use a function which holds a reference to whichever button I'm trying to use. My prototype's mouseDownFunc is then called, it checks the boundary of the button instance's dimensions, and then finally calls a referenced onPress() (which is actually an overridden method that every button uses, so each button has a custom set of instructions when pressed).
So, if you're still following along (I know, it's a bit confusing without seeing the full script), the problem is that because my event listeners are using the same function, they're overwriting the previous event listener, so only the last button added functions correctly. To sum this all up, how can I add multiple event listeners to the canvas element, which all use the same function without erasing the previous event listeners. Note that I'm trying to do this without the use of jQuery or other third-party extensions.
If more information is needed in regards to my code so that it's easier to understand, let me know. Thanks in advance for any type of feedback.
Edit: Perhaps this might help. Note that this isn't the complete code, but contains the main points:
Adding a button:
this.test_button = new CreateButton(this, 'test_button');
this.test_button.onPress = function() {
alert('Blue button works!');
};
this.test_button.create(200, 50, 30, 200, 'text');
When using create() on a button, variables are checked and stored, as well as an array that holds onto all current buttons (so they can be referenced at any point). Then this is run: this.that.custom_canvas.addEventListener('mousedown', this.create.prototype.mouseDownFunc, false);
When mouseDownFunc is called, this takes place:
CreateButton.prototype.create.prototype.mouseDownFunc = function(e) {
var mouse_x = e.clientX-this.offsetLeft;
var mouse_y = e.clientY-this.offsetTop;
// Check if the mini-button button was actually clicked
if(mouse_x >= test2.x && mouse_y >= test2.y && mouse_x <= (test2.x + test2.width) && mouse_y <= (test2.y + test2.height)){
alert('clicked and working!');
test2.onPress(); // A reference to the saved function
}
};
Currently, my test2 is a reference to any given object -- it's a global var, but after I get these other issues fixed, I'll deal with getting rid of the global var (I know, I know - it was just a temporary quick-fix).
Maybe instead of an event listener for each and every possible button, and checking box size within the function, you could create a single event that calls a routing function to check where on the element the event occurred, and then delegate to another function
You need to design something to handle the event dispatch in your program. You seem to have components that have their listeners all disorganized. You could build a tree data structure (https://en.wikipedia.org/wiki/Tree_%28data_structure%29) that is a hierarchy for the event dispatch in your components ( such as buttons, text areas etc.). The idea is that when the tree is traversed the events will be handled in an ordered fashion. The tree would be reorganized based on how the user interacts with your program. For a simple example, to start this tree could perhaps prioritize the most recently drawn component (out of some structure that holds a list of everything to be drawn) as the event listener to receive event handling first. Then, if a component is blocked by another component the blocked component (like a button covering the button) it's event handling could either be disabled or scheduled to happen later depending on your implementation. Of course your implementation may be more complex, but you need to keep track the event handling. Java uses a component heirarchy data structure to handle a wide variety of GUI events that you can learn more about here http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html