Skip to content

Instantly share code, notes, and snippets.

@codenamejason
Created December 6, 2017 15:33
Show Gist options
  • Save codenamejason/d08c701e0de940216f33e26c2d3f0ecb to your computer and use it in GitHub Desktop.
Save codenamejason/d08c701e0de940216f33e26c2d3f0ecb to your computer and use it in GitHub Desktop.
Knockout custom if/else virtualElements
// Add handling of if / else for nested virtual elements
function startsCommentBinding(node) {
return (node.textContent || node.innerText)
.match(/^(<!--)?\s*ko\s+[\s\S]+/);
}
function endsCommentBinding(node) {
return (node.textContent || node.innerText)
.match(/^(<!--)?\s*\/ko/);
}
function getBindingConditional(node, bindingContext) {
var conditional;
var bindings = ko.bindingProvider.instance.getBindings(node, bindingContext);
if (!bindings) {
return
} else if (conditional = bindings['if']) {
return ko.pureComputed(function () {
return !ko.unwrap(conditional)
});
} else if (conditional = bindings['ifnot']) {
return ko.pureComputed(function () {
return ko.unwrap(conditional)
});
} else if (conditional = bindings['foreach']) {
return ko.pureComputed(function () {
var conResult = ko.unwrap(conditional);
return !conResult || conResult.length === 0;
});
}
return
}
function prevVirtualNode(node) {
var depth = 0;
do {
if (node.nodeType === 8) {
if (startsCommentBinding(node)) {
if (--depth == 0) {
return node;
}
} else if (endsCommentBinding(node)) {
depth++;
}
}
} while (node = node.previousSibling);
return;
}
function getPrecedingConditional(element, bindingContext) {
var prevSib = element;
do {
prevSib = prevSib.previousSibling;
} while (prevSib && [1, 8].indexOf(prevSib.nodeType) === -1);
if (!prevSib) {
throw new Error("Unmatched conditionals!");
return;
}
if (prevSib.nodeType == 1) {
return getBindingConditional(prevSib, bindingContext);
} else if (prevSib.nodeType == 8) {
// The first previous adjacent node is a comment node.
return getBindingConditional(prevVirtualNode(prevSib), bindingContext);
}
throw new Error("No.")
}
ko.bindingHandlers['else'] = {
init: function (element, va, ab, vm, bindingContext) {
var elseConditional,
openComment,
closeComment,
firstChild,
nextChild,
lastChild;
elseConditional = getPrecedingConditional(element, bindingContext);
if (!elseConditional) {
throw new Error("An else binding had no sibling conditional.");
}
firstChild = ko.virtualElements.firstChild(element);
nextChild = firstChild;
do {
lastChild = nextChild;
nextChild = ko.virtualElements.nextSibling(nextChild);
} while (nextChild);
openComment = document.createComment('ko if: __elseCondition__');
closeComment = document.createComment('/ko')
ko.virtualElements.prepend(element, openComment);
ko.virtualElements.insertAfter(element, closeComment, lastChild);
ko.applyBindingsToDescendants(bindingContext.extend({
__elseCondition__: elseConditional
}), element);
return {
controlsDescendantBindings: true
};
}
}
ko.virtualElements.allowedBindings['else'] = true;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment