问题
This is what I currently have:
http://plnkr.co/edit/L9XqEyOtRSGHc9rqFPJX
I am trying to make it so that when I press the down key for the first time, the focus will move onto #div2
; repeating the action should have the focus moving onto li > div:first-child
.
In the demo however, upon the first time the down key is pressed, the focus will jump to li > div:first-child
directly, because both the key-map
on #div1
and #div2
captured the same keydown event, so the focus jumps from #div1
to #div2
then #div2
to li > div:first-child
in one go.
How should I solve this issue or, better yet, is there anyway to improve how the codes are structured? I am not sure whether those event listeners are attaching to the DOM in the optimal fashion right now.
回答1:
You are attaching a keydown
handler to the document for each element. I don't think stopPropagation()
will do any good because the two handlers are on the same element and it won't propagate up from document but both will still fire.
I suggest re-evaluating how you're approaching it. You only wish the element with the focus class to have it's options evaluated, so why not wrap all those elements in an element with your directive and have it listen only once and choose the element to act on.
(plunker)
<div key-mapped="">
<!-- children will apply key map of element with focus class -->
<div id="div1" class="focus" key-map="{
40: '#div2'
}">Hello</div>
directive:
}).directive('keyMapped', function($rootScope) {
return {
restrict: 'A',
link: function(scope, element, attr) {
angular.element(document).bind('keydown', function(event) {
var target = $(element).find('.focus');
console.log('target: ' + target);
var options = scope.$eval(target.attr('key-map'));
----EDIT----
Someone let me know if it's not a good practice, but you could always put the event handler on your directive object and ensure it is only set once and send a custom event to the element with the 'focus' class that you bind to in your link function.
(plunker)
}).directive('keyMap', function($rootScope) {
var dir = {
onKeydown: function(event) {
var element = document.querySelector('.focus');
var newEvent = new CustomEvent("keyMapKeydown", {
detail: { keyCode: event.keyCode },
bubbles: false,
cancelable: true
});
element.dispatchEvent(newEvent);
},
registered: false, // ensure we only listen to keydown once
restrict: 'A',
link: function(scope, element, attr) {
// register listener only if not already registered
if (!dir.registered) {
dir.registered = true;
angular.element(document).bind('keydown', dir.onKeydown);
}
var options = scope.$eval(attr.keyMap);
// listen for custom event which will be dispatched only to the
// element that has the 'focus' class
element.bind('keyMapKeydown', function(event) {
var keyCode = event.detail.keyCode;
if (options && (keyCode in options)) {
element.removeClass('focus');
angular.element(document.querySelector(options[keyCode])).addClass('focus');
event.stopPropagation();
}
});
}
};
回答2:
You need to stop the propagation of events. Events propagate upwards. In your directive put something like the following:
$(elm).click(function (event) {
event.stopPropagation();
});
Or, you could create a stop-propagation directive itself. This SO question has a great way of doing it.
来源:https://stackoverflow.com/questions/17439530/angularjs-directive-how-to-let-an-event-only-be-triggered-once-if-otherwise-it