I'm making some tests for using (or not) web components in a single page app I'm creating.
Here's an example for the problem:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<template id="aTemplate">
<div style="border:1px solid red">
<p>text <input type="text"></p>
<button>ClickMe</button>
</div>
</template>
<script>
var Proto = Object.create(HTMLElement.prototype);
Proto.createdCallback = function () {
var t = document.querySelector('#aTemplate');
var clone = document.importNode(t.content, true);
this.createShadowRoot().appendChild(clone);
};
Proto.aFunction = function() {
alert("proto " + "text value?");
}
document.registerElement('x-proto', {prototype: Proto});
var ProtoChild = Object.create(Proto);
ProtoChild.createdCallback = function () {
Proto.createdCallback.call(this)
};
ProtoChild.aFunction = function() {
alert("child " + "text value?");
}
document.registerElement('x-proto-child', {
prototype: Proto
});
</script>
<x-proto></x-proto>
<x-proto-child></x-proto-child>
</body>
The problem is that I cannot find a way to set a "onclick" handler in the button (inside the template) that calls the method "aFunction" in the object created using the prototype. The method should be called in the correct object instance, with access to the internal DOM components, and the attributes and functions in the prototype.
I've tried a lot of things, (binding the event after construction, using JS or JQuery, using the created/attached callbacks, a ) but I'm out of ideas.
Thanks in advance.
EDIT:
Thanks to MinusFour for the answer. The line:
clone.querySelector('button').addEventListener('click', this.aFunction);
in what I was trying to do, anyway, resulted in (same but with JQuery, for testing compatibilitiy):
$(this.showButton).on("click", this.aFunction.bind(this));
The "bind" makes 'this' AKA the container, the complete component, available in JS code, what I needed.
Here's the completed final example, someone may find it helpful:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
</head>
<body>
<template id="aTemplate">
<div style="border:1px solid darkgray;padding:10px;margin: 10px;">
<h2 class="theCaption"></h2>
<p>text <input class="theText" type="text"></p>
<button class="showButton">Show val</button>
<button class="closeButton">Close</button>
</div>
</template>
<script>
// The .bind method from Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
function createProto() {
$("#spawnPoint").append("<x-proto x-caption='proto'></x-proto>");
}
function createChild() {
$("#spawnPoint").append("<x-proto-child x-caption='a child of proto'></x-proto-child>");
}
var Proto = Object.create(HTMLElement.prototype);
Object.defineProperty(Proto, "x-caption", {value: "no caption"});
Proto.createdCallback = function () {
var t = document.querySelector('#aTemplate');
var clone = document.importNode(t.content, true);
this.shadowRoot = this.createShadowRoot();
this.shadowRoot.appendChild(clone);
$(clone).children("div").append("<p>ssss</p>")
this.showButton = this.shadowRoot.querySelector('.showButton');
this.closeButton = this.shadowRoot.querySelector('.closeButton');
this.shadowRoot.querySelector('.theCaption').textContent = $(this).attr("x-caption");
this.theText = this.shadowRoot.querySelector('.theText');
$(this.showButton).on("click", this.aFunction.bind(this));
$(this.closeButton).on("click", this.close.bind(this));
};
Proto.aFunction = function () {
alert("in proto = " + $(this.theText).val());
}
Proto.close = function () {
$(this).detach();
}
document.registerElement('x-proto', {prototype: Proto});
var ProtoChild = Object.create(Proto);
ProtoChild.createdCallback = function () {
Proto.createdCallback.call(this);
};
ProtoChild.aFunction = function () {
alert("overrided in child = " + $(this.theText).val());
}
document.registerElement('x-proto-child', {
prototype: ProtoChild
});
</script>
<button onclick="javascript:createProto()">Create proto</button>
<button onclick="javascript:createChild()">Create child</button>
<div id="spawnPoint">
</div>
</body>
I believe you could just add the listener from the importedNode
(clone
in your case).
clone.querySelector('button').addEventListener('click', function(){
//code logic here
});
You also probably meant:
document.registerElement('x-proto-child', {
prototype: ProtoChild
});
Here's how it would look like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<template id="aTemplate">
<div style="border:1px solid red">
<p>text <input type="text"></p>
<button>ClickMe</button>
</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.14/webcomponents.min.js"></script>
<script>
var Proto = Object.create(HTMLElement.prototype);
Proto.createdCallback = function () {
var t = document.querySelector('#aTemplate');
var clone = document.importNode(t.content, true);
clone.querySelector('button').addEventListener('click', this.aFunction);
this.createShadowRoot().appendChild(clone);
};
Proto.aFunction = function() {
alert("proto " + "text value?");
}
document.registerElement('x-proto', {prototype: Proto});
var ProtoChild = Object.create(Proto);
ProtoChild.createdCallback = function () {
Proto.createdCallback.call(this);
console.log(this.template);
/*this.template.querySelector('button').addEventListener('click', function(){
console.log('child');
});*/
};
ProtoChild.aFunction = function() {
alert("child " + "text value?");
}
document.registerElement('x-proto-child', {
prototype: ProtoChild
});
</script>
<x-proto></x-proto>
<x-proto-child></x-proto-child>
</body>