Using Durandal,
app.showMessage(message, title);
shows a modal dialog with the supplied message and title, and a single primary button OK. All this is styled by Bootstrap.
It doesn't seem to respect the keyboard. Clicking the OK button with the mouse works as expected, but neither the ESC nor ENTER keys clicks the OK button.
I have yet to find anything useful in the rather Spartan Durandal documentation, but sometimes that means it's covered by the documentation of a subsystem such as Knockout or Bootstrap.
Can anyone suggest a strategy or some pertinent reading on the customary browser independent way to bind buttons to keyboard shortcuts?
I find it so hard to believe that Durandal dialogs ignore the keyboard that I wonder whether I've screwed something up.
For a few moments I thought I had an answer in CSS specified behaviour. It was even specified in a W3C document. But it's obsolete and not supported.
No doubt I can present a view and view model modally. This will give me a chance to bind additional behaviour, and I can pass parameters, pretty much plug-in replacing app.showMessage() but it all seems rather heavy handed.
I noticed today that spacebar closes message dialogs. Presumably with only one focusable control, that's where the focus is. Given the Durandal habit of minimalism this might even be by design, it's hard to say.
You can use jwerty, which can be found here. We use jwerty in our Durandal project (as does Ayende Rahien in RavenDB's new HTML5 Raven Studio, which is also written with Durandal).
To see the rich keyboard support with jwerty in action, take a look at this video on my DropBox account of a Dropdown Datepicker that I wrote entirely with Durandal (and jwerty).
If you're interested, I can show you how to integrate jwerty into Durandal (pretty trivial).
[EDIT]
There are two strategies for using jwerty with Durandal: inline and custom Knockout binding.
INLINE
Below is a snippet directly from our CalendarNavigationEngine:
//Bind the directional keys
CalendarNavigationEngine.prototype.bindDirectionalKeys = function () {
var that = this;
var kContainerClass = this.kContainerClass;
//Bind directional keys
jwerty.key('left', function () { that.prevItem(); }, kContainerClass);
jwerty.key('up', function () { that.prevWeek(); }, kContainerClass);
jwerty.key('pgup / ctrl+left', function () { that.prevPage(); }, kContainerClass);
jwerty.key('right', function () { that.nextItem(); }, kContainerClass);
jwerty.key('down', function () { that.nextWeek(); }, kContainerClass);
jwerty.key('pgdown / ctrl+right', function () { that.nextPage(); }, kContainerClass);
jwerty.key('home', function () { that.firstItem(); }, kContainerClass);
jwerty.key('end', function () { that.lastItem(); }, kContainerClass);
jwerty.key('enter / tab', that.select.bind(that), kContainerClass);
};
We usually call bindDirectionalKeys Durandal's compositionComplete handler on the viewModel. In this case, the call chain is much deeper as CalendarNavigationEngine is not a viewModel.
By inline I mean to say that you are making the call directly from your viewModel (or module), as shown above. kContainerClass is the DOM class on the element, or elements, that has focus and should receive and process keyboard events.
With this approach, you need a corresponding unbind method:
//Unbind key bindings
CalendarNavigationEngine.prototype.clearKeyBindings = function () {
$(this.kContainerClass).unbind('keydown.jwerty');
};
This would [eventually] be called from Durandal's detached handler, and cleans up all key bindings in the context of kContainerClass to avoid memory leaks. THIS IS NOT OPTIONAL.
CUSTOM KNOCKOUT BINDING
There are plenty of resources on StackOverflow on how to write custom Knockout bindings. But basically, the strategy involves binding the DOM element to a jwerty key binding. This is pretty trivial, although we did not take this approach (we are considering it).
There is one caveat: Knockout does not permit multiple identical bindings. So you cannot call a jwerty binding multiple times, which is what you would need to do in order to associate multiple key bindings with a single element (as in the example I gave above). However, what you can do is bind an array of key bindings to a single element, and then just have your custom Knockout binding, in its init handler, iterate over that array, binding with jwerty for each item. Again, still trivial.
Related
There are many contentcontrols in a document and I need to find out a way that the cursor is in which content control so that I will select that control and do the operation accordingly. I think by implementing onEnter and onExit events for contentcontrols , I can achieve it. But I don't know how to declare and invoke those eventhandlers in JavaScript API. Any help is really appreciated.
You would need to use a combination of APIs to implement that functionality with the current API set:
First add an event handler for the Document.selectionChanged event.
Every time the event fires, get the Range object corresponding to the selection in the document, using the Document.getSelection() API.
Check the range to see if there's a content control in it, using the Range.contentControls relationship.
-Michael (PM for add-ins)
Good question! We do have an onEnter event for content controls (we call it binding.selectionChanged. We also have a binding.dataChanged event who gets triggered if the user changes the content and exits the content control
so an alternative solution to what Michael proposed is to create bindings for each content control in the document and then register for such events.
you can achieve this by:
1. traversing the content control collection.(use body.contentControls collection)
2. for each content control, grab or set the title and use it to create a binding by named item. check the bindings.addFromNamedItem method.
3. on the callBack make sure to subscribe to the selectionChanged (or DataChanged) for the binding.
the create binding code and register to the events will look like this:
function CreateCCSelectionChangedEvent() {
Office.context.document.bindings.addFromNamedItemAsync("TitleOfTheContentControl", { id: 'Binding01' }, function (result) {
if (result.status == 'succeeded') {
result.value.addHandlerAsync(Office.EventType.BindingSelectionChanged, handler);
}
});
}
function handler() {
console.log("Event Triggered!");
}
Hope this helps!
Michael,
My company tried this approach a few years ago in a VSTO addin. It ended badly. The problem is the number of events you have to handle is horrific. The performance penalty is drastic and grows with the document size.
The old event management in which each handler for specific actions was directly attached to the target element is becoming outdated, since considerations about performance and memory saving started spreading in the developers community.
Event delegation implementations had an acceleration since jQuery updated the old fashioned .bind() and .live() methods with the new .on() method to allow delegation.
This determines a change in some seasoned approaches, where to use event delegation a rework is necessary.
I am trying to work out some best practice while keeping the coding style of my library, and looked for similar situations faced from other developers to find an answer.
Using OOP with functions as constructors, I usually have interfaces for objects creation like this:
var widget = new Widget({
timeout: 800,
expander: '.expanders'
});
with object literals given as argument, providing a clean map of names and values of the input passed. The class underlying this code could be something like the following:
var Widget = function(options) {
// some private members
var _timeout;
var _$widget;
var _$expander;
// methods
this.init = function() {
_timeout = options.timeout || 500;
_$expander = $(options.expander);
_$widget = _$expander.next();
_$expander.on('click', _toggle);
};
var _toggle = function(e) {
if (_$widget.is(':visible')) {
_$widget.hide(_timeout);
} else {
_$widget.show(_timeout);
}
};
this.init();
};
Using "private" methods gave me some benefits in terms of code readability and cleanness (only useful methods are publicly exposed to the user), beyond the small gain in performance (each scope resolution takes more time than a local variable). But when speaking about event handlers, it clashes with the event delegation paradigm.
I thought to make public the methods that I used to associate internally to the listeners in the class:
this.toggle = function(e) {
if (_$widget.is(':visible')) {
_$widget.hide(_timeout);
} else {
_$widget.show(_timeout);
}
};
then driving externally, or in another proxy class, the proper delegation with something like this:
var widget = new Widget({
expander: '.expanders'
});
$(delegationContext).on('click', '.expanders', widget.toggle);
but it did not seem to me the best approach, failing in the exposure of a non-useful method in the interface, so I tried a way to let the main class know directly all the information to delegate the event autonomously, through the interface:
var widget = new Widget({
timeout: 800,
expander: {
delegationContext: '.widgetContainer',
selector: '.expanders'
}
});
which would allow to keep on using private methods internally in the class:
var $context = $(options.expander.delegationContext);
$context.on('click', options.expander.selector, _toggle);
What are your practice and suggestions about it?
And what are the main trends of other developers you heard about as far as today?
it clashes with the event delegation paradigm.
Forget that paradigm! Don't choose it because it's "cool" and directly attaching events "is becoming outdated", choose it only when it is necessary. Event delegation is not necessary for you, nor does it speed up anything. It is not a solution you can apply - you have no problem!
You have one element and a single element-specific event handler function (every widget expander has its own function). You can already infer from your problems applying event delegation that you cannot and should not use it here. Attach the handlers directly.
Event delegation is only useful when you have a vast amount of similar elements, located consistently in the DOM with one common ancestor, that would all going to be attached the same event handler function. This is not the case with your Widget constructor that does take instance-specific options such as the expander selector.
In your current Widget class, using event delegation would clash with the single responsibility principle. In the event delegation paradigm, you would need to see the elements in the delegation context as a whole, being as homogeneous as possible. The more element-specific data and state you add, the more delegation advantages you are loosing.
If you really want to use event delegation here, I would suggest something like
var Widgets = {
init: function(delegationContext) {
$(delegationContext).on("click", ".widget-expander", function(e) {
$this = $(this);
$this.next().toggle($this.data("widget-expander-timeout") || 500);
});
},
activate: function(options) {
$(options.expander)
.addClass("widget-expander")
.data("widget-expander-timeout", options.timeout);
}
};
Here no constructors are used. You just initialise the event delegation context, and then you can add single elements to be captured by the delegation mechanism. All data is stored on the element, to be accessible from the event handler. Example:
Widgets.init('.widgetContainer');
Widgets.activate({expander: '.expanders', timeout: 800});
And what are the main trends of other developers you heard about as far as today?
Apart from that question being off-topic on StackOverflow, I can only advise you not to follow every trend someone heard about. Learn about new (or advertised or fancy) technologies, yes, but do not forget to learn about when to use them.
I'm trying to create an algorithm for an event based editor like in StarCraft 2 Editor that can support:
Create UI
Play sounds
Handle keyboard/mouse inputs
Display messages
Button(or some referenced UI object) is pressed etc.
Pretty much the same thing as in StarCraft 2 Editor (of course not the 3D stuff too)
So far I'm thinking to use JSON , add every event in an object and then loop through them and create an event using the addEventListener() method.
The JSON Events Object(of course it will be created by the user in the editor with no programming):
var Events={
//your event's names here
onReady:{ //on page ready to manipulate
displayMessage:{//just a simple popup
text:"Hello user!",
title:"Welcome!",
type:"normal",
},
createButton:{ //creates a buton on the screen
text:"Click me!",
id:"myButton"
}
},
onClick:{
id:"myButton" ,//the id of the button we just created
actions:{ //the actions applied after we click the button
displayMessage:{//just a simple popup
text:"You pressed me!",
title:"Button",
type:"error",//show the message as an error
}
}
}
}
I found some softwares (GameMaker,Construct 2,GameDevelop) that have an event based editor if you would like to get an idea about what I'm talking about (if you don't already know about StarCraft 2 Editor)
My question is:
What is the best algorithm that I can use to achieve this?
Sounds like a job for jQuery UI.
When the user creates a custom area in your editor all it's attributes are stored inside an object (that you can save as JSON) that would then be applied to a div as param when loading the map (using html-attributes.
function create_areas(areas){
var map = $('#map_area');
for(var i=0;i<areas.length;i++){
map.append($('<div>', area[i].params));
}
}
whereas params would look something like this:
params = {
width: 100,
height: 200,
....
mousedown: function(){ play_music('hello'); },
keydown: function(e){ alert('you pressed ' + e.keyCode; }
}
also the jQuery UI tools like draggable and resizeable should ease up building your editor.
I'd model this more after backbone's event system:
events: {
'click selector': handler,
'mouseover selector': handler2,
...
}
Handlers can be any javascript function, this would allow you to create a bunch of pre-defined functions like displayMessage.
Then you could curry your own handlers, which would allow your users to specify configuration if they need it.
Example:
var events = {
'click element': displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
'mouseover pizza': createButton({...})
}
function displayMessage(options) {
var options = options;
return function() {
//display message logic
}
}
Then you can supply a compose function among other helpers (look up promises perhaps?) to combine your functions together:
var events = {
'click element': compose(
displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
createButton({})
),
'mouseover pizza': createButton({...})
}
This could work out?
Caveat: it might be better if events was an array that contained objects. That way you can have multiple click handlers on some selector without collisions.
The way I see this there are really severall choices you need to make. I would, although I prefer JSON as a data construct not limit myself to this subset of an actuall programming language. And engener this the other way around.
You have events, handlers and options. Where a option, or better a option list is the user inputed data, the handlers are the actual action, and the events are triggers to set some action off.
If you read this carefully you will notice this is the exact description of the basic structure of most jQuery-Scripts or Event-Driven Software in generall. Only the users options in jQuery are (since it is a DOM Framework) most often the context of a single DOM-Element. So, here we are and I would suggest to simply borrow the theorie behind this and make use of promisses wich make a very clear and great way to generate code!
So my call to any event chain would look like this.
...when(chainObject['event'])
.then(function(event) {
//call handler
handlers[chainObject[selectedHandler]].call(event.context, chainObject['options']);
//apply next element(s) in chain, this is the current promise
appendNextElement(chainObject['followingHandlers'], this);
})...
Notice how apply makes it easy for you to change the environement and in turn behaviour of any hanlder based on what the user and event did. And promisses make error handling very easy!
This of course applies to only one node in your chain. So what should a data structure look like to let you generate this kind of code?
One node in your structure would look like this:
{
event: 'click',
selectedHandler: 'sohwText',
options: {
'text': 'helloWorld'
},
followingChain: {...OTHER HANDLERS....}
}
The important thing to notice is that like a good structured functional programm you are looking at a tree and not at a simple list of events. So every actual DOM Element holds many of these
var eventTree = {
'.someButton': [..Handlers of this button...],'
'.someOtherButton': [..Handlers of the other button...],
}
And there we go. You have a context (the button), a event, user input and a handler.
The resulting app should not only work, but will be styled for any experienced JavaScript-Programmer to expand or mod.
I want to know if I add an event listener to a button, do I have to remove it on unload? Would pressing the 'back' button automatic removes everything current page elements in which I don't need to worry about memory leaks?
(function () {
"use strict";
ui.Pages.define("/pages/registraton/registraton.html",{
ready: function (element, options) {
document.getElementById("submitRegister").addEventListener(
"click", postRegistration , false);
},
unload: function () {
document.getElementById("submitRegister").removeEventListener(
"click", postRegistration, false);
}
});...
Thanks in advance.
You need to worry about memory leaks in the single-page navigation model that the WinJS.Navigation namespace promotes.
The model you've set up -- where by you implement unload -- is definitely the right approach. How complex & deep you want to get depends on the complexity of your application. Specifically, if you have multiple controls, with multiple manual event handlers you may want to create a set of helpers to enable you to clean up those handlers in one swoop. This may be as simple as pushing element, event name, and the handler instance into an array when when leaving that page and destroying/removing it from the DOM, you can just burn through the array removing the items that need to be cleaned up.
Note that you need to only need to explicitly clean up the case where the handler, and the DOM object have different life times. If they go away together -- e.g. a control attached to a DOM element in the page then you don't have to clean up the everything explicitly. The Garbage Collector will eventually clean it up. If you are a particularly memory heavy application, you may get some wins here by removing the listeners more aggressively.
There are some other things to remember:
This also applies to pure javascript objects that implement the addEventListener contract i.e. the list view
Don't use attachEvent -- it's going to cause unbreakable cycles due to it's old implementation under the covers. It is actually a deprecated API, so shouldn't be used anyway
Be wary when you supply event handlers where you've bound the this pointer, when your trying to unbind them. E.g.
Example:
var element = getInterestingElement();
element.addEventListener("click", this.handleClick.bind(this));
If you try to detach the event, you're lost -- the return valud from the .bind() is lost in the wind, and you'll never be able to unhook it:
var element = getInterestingElement();
element.removeEventListener("click", this.handleClick); // Won't remove the listener
element.removeEventListener("click", this.handleClick.bind(this)); // Won't remove, since it's a different function object
The best solution here is to either monkey patch handleClick before attaching it:
this.handleClick = this.handleClick.bind(this);
Or store it away for later use:
this.handlerClickToCleanup = this.handleClick.bind(this);
element.addEventListener("click", this.handleClickToCleanup);
JQuery has great support for custom events - .bind("foo", function(e).... However what if the mechanic of triggering the event is not ready yet and has to be constructed only on those elements that have the event bound on?
For example I want a scrollin event that gets fired when an element is scrolled into a viewport. To do this, I would onscroll have to check all the elements and trigger scrollin on those that were outside the viewport and now are inside. This is not acceptable.
There are some tricks to speed it up. For example one of the plugins for this checks all the elements in "private" $.cache and does the checking only on those that have scrollin event bound.
But that's also ugly. What I need is an additional callback for the binding of the event (additional to the callback for handling) that would take care of the scroll management, that is to put the element(s) into some elementsCheckOnScrol cache array.
I'm looking for something like:
$.delegateBind("scrollin", function(jqSelection) { ... });
element.bind("scrollin", function(e) {..}); //Calls ^ after internal bind management
Edit: This would be nice api!
$.bind("bind", function(onWhat) { ... })
:-)
If I'm not misunderstanding you, you could patch the bind method like this:
(function($) {
var oldBind = $.fn.bind;
$.fn.bind = function(name) {
if(name === "scrollin") {
delegateFunction(this);
}
oldBind.apply(this, arguments);
};
})(jQuery);
What it does is checking whether a scrollin is being bound, and if so, calls your delegate function. After that it simply calls the original bind function which does all jQuery things like it does regularly.
After having added this code, you could use it like this: http://jsfiddle.net/pimvdb/g4k2G/.
function delegateFunction(selection) {
alert(selection.length);
}
$('a').bind('scrollin', function() {});
Note that this does not support object literals being passed to .bind (only (name, func)), but you could implement that as well.
I found an $.event.special API, but I don't know "how much" public it is. It is not in the docs and has been changed at least once before. http://benalman.com/news/2010/03/jquery-special-events/