Skip to content

Commit

Permalink
Add delegated<Event>Bubble property
Browse files Browse the repository at this point in the history
For every type of event an additional property can be used, denoted by
`delegated<Event>Bubble`, which can be set to true to explicitly allow
bubbling in scenarios where it is needed. This relates to a previous bug
detailed here:
#7

A common use case of this is for example on Bootstrap dropdown menus,
which require the event to bubble in order for the dropdown to properly
capture the click event and close as expected.
  • Loading branch information
Craga89 committed Nov 28, 2014
1 parent 6accc88 commit 0187f55
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 19 deletions.
28 changes: 19 additions & 9 deletions build/knockout-delegatedEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}
}(function(ko, actions) {
var prefix = "ko_delegated_";
var createDelegatedHandler = function(eventName, root) {
var createDelegatedHandler = function(eventName, root, bubble) {
return function(event) {
var data, method, context, action, owner, matchingParent, command, result,
el = event.target || event.srcElement,
Expand Down Expand Up @@ -97,25 +97,32 @@
}
}

//prevent bubbling
event.cancelBubble = true;
if (typeof event.stopPropagation === "function") {
event.stopPropagation();
//prevent bubbling if not enabled
if(bubble !== true) {
event.cancelBubble = true;
if (typeof event.stopPropagation === "function") {
event.stopPropagation();
}
}
}
}
};
};

//create binding handler name from event name
var createBindingName = function(eventName) {
return "delegated" + eventName.substr(0, 1).toUpperCase() + eventName.slice(1);
};

//create a binding for an event to associate a function with the element
var createDelegatedBinding = function(event) {
var bindingName;
if (!event) {
return;
}

//capitalize first letter
bindingName = "delegated" + event.substr(0, 1).toUpperCase() + event.slice(1);
//get binding name
bindingName = createBindingName(event);

//create the binding, if it does not exist
if (!ko.bindingHandlers[bindingName]) {
Expand All @@ -130,16 +137,19 @@

//add a handler on a parent element that responds to events from the children
ko.bindingHandlers.delegatedHandler = {
init: function(element, valueAccessor) {
init: function(element, valueAccessor, allBindings) {
var events = ko.utils.unwrapObservable(valueAccessor()) || [];

if (typeof events === "string") {
events = [events];
}

ko.utils.arrayForEach(events, function(event) {
//check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get( createBindingName(event + 'Bubble') ) === true;

createDelegatedBinding(event);
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element));
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble));
});
}
};
Expand Down
2 changes: 1 addition & 1 deletion build/knockout-delegatedEvents.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions spec/knockout-delegatedEvents.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,28 @@ describe("knockout-delegatedEvents", function(){
expect(vm.bubbled).toBeTruthy();
});
});

describe("when event has bubbling explicitly enabled", function() {
it("should allow bubbling even when event is handled", function() {
var vm = {
myAction: defaultAction,
bubbled: false
};

root.setAttribute("data-bind", "");
parent.setAttribute("data-bind", "delegatedHandler: ['click'], delegatedClickBubble: true");
child.setAttribute("data-click", "myAction");

ko.utils.registerEventHandler(root, "click", function() {
vm.bubbled = true;
});

ko.applyBindings(vm, root);
ko.utils.triggerEvent(child, "click");

expect(vm.bubbled).toBeTruthy();
});
});
});
});
});
28 changes: 19 additions & 9 deletions src/knockout-delegatedEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}
}(function(ko, actions) {
var prefix = "ko_delegated_";
var createDelegatedHandler = function(eventName, root) {
var createDelegatedHandler = function(eventName, root, bubble) {
return function(event) {
var data, method, context, action, owner, matchingParent, command, result,
el = event.target || event.srcElement,
Expand Down Expand Up @@ -96,25 +96,32 @@
}
}

//prevent bubbling
event.cancelBubble = true;
if (typeof event.stopPropagation === "function") {
event.stopPropagation();
//prevent bubbling if not enabled
if(bubble !== true) {
event.cancelBubble = true;
if (typeof event.stopPropagation === "function") {
event.stopPropagation();
}
}
}
}
};
};

//create binding handler name from event name
var createBindingName = function(eventName) {
return "delegated" + eventName.substr(0, 1).toUpperCase() + eventName.slice(1);
};

//create a binding for an event to associate a function with the element
var createDelegatedBinding = function(event) {
var bindingName;
if (!event) {
return;
}

//capitalize first letter
bindingName = "delegated" + event.substr(0, 1).toUpperCase() + event.slice(1);
//get binding name
bindingName = createBindingName(event);

//create the binding, if it does not exist
if (!ko.bindingHandlers[bindingName]) {
Expand All @@ -129,16 +136,19 @@

//add a handler on a parent element that responds to events from the children
ko.bindingHandlers.delegatedHandler = {
init: function(element, valueAccessor) {
init: function(element, valueAccessor, allBindings) {
var events = ko.utils.unwrapObservable(valueAccessor()) || [];

if (typeof events === "string") {
events = [events];
}

ko.utils.arrayForEach(events, function(event) {
//check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get( createBindingName(event + 'Bubble') ) === true;

createDelegatedBinding(event);
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element));
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble));
});
}
};
Expand Down

0 comments on commit 0187f55

Please sign in to comment.