HTML5/webcomponents: 从模板代码中调用原型函数
HTML5/webcomponents: Call prototype function from template code
我正在对正在创建的单页应用程序中使用(或不使用)Web 组件进行一些测试。
这里有一个问题的例子:
<!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>
问题是我找不到在按钮(模板内)中设置 "onclick" 处理程序的方法,该处理程序调用使用原型创建的对象中的方法 "aFunction"。该方法应该在正确的对象实例中调用,可以访问内部 DOM 组件,以及原型中的属性和函数。
我尝试了很多方法,(在构造后绑定事件,使用 JS 或 JQuery,使用 created/attached 回调,a)但我没有想法。
提前致谢。
编辑:
感谢 MinusFour 的回答。该行:
clone.querySelector('button').addEventListener('click', this.aFunction);
无论如何,我尝试做的结果是(相同但 JQuery,用于测试兼容性):
$(this.showButton).on("click", this.aFunction.bind(this));
"bind" 使 'this' 又名容器,完整的组件,在 JS 代码中可用,正是我所需要的。
这是完成的最后一个例子,可能有人会觉得有用:
<!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>
我相信您可以从 importedNode
添加监听器(在您的情况下是 clone
)。
clone.querySelector('button').addEventListener('click', function(){
//code logic here
});
你可能还想说:
document.registerElement('x-proto-child', {
prototype: ProtoChild
});
这是它的样子:
<!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>
不久前我遇到了类似的问题:
http://www.davevoyles.com/accessing-member-functions-in-polymer/
您是在使用 Polymer,还是只使用 Web Components?
只要将您尝试调用的函数包装在聚合物就绪事件中,就可以开始了,并且可以从您的聚合物元素中调用函数。
Polymer parses element definitions and handles their upgrade
asynchronously. If you prematurely fetch the element from the DOM
before it has a chance to upgrade, you’ll be working with a plain
HTMLElement, instead of your custom element.
或者,您可以根据需要查询自定义元素(通过 ID、class、attr 或元素名称)并获得相同的结果。这是他的例子:http://jsbin.com/qikaya/2/edit
我正在对正在创建的单页应用程序中使用(或不使用)Web 组件进行一些测试。
这里有一个问题的例子:
<!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>
问题是我找不到在按钮(模板内)中设置 "onclick" 处理程序的方法,该处理程序调用使用原型创建的对象中的方法 "aFunction"。该方法应该在正确的对象实例中调用,可以访问内部 DOM 组件,以及原型中的属性和函数。
我尝试了很多方法,(在构造后绑定事件,使用 JS 或 JQuery,使用 created/attached 回调,a)但我没有想法。
提前致谢。
编辑:
感谢 MinusFour 的回答。该行:
clone.querySelector('button').addEventListener('click', this.aFunction);
无论如何,我尝试做的结果是(相同但 JQuery,用于测试兼容性):
$(this.showButton).on("click", this.aFunction.bind(this));
"bind" 使 'this' 又名容器,完整的组件,在 JS 代码中可用,正是我所需要的。
这是完成的最后一个例子,可能有人会觉得有用:
<!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>
我相信您可以从 importedNode
添加监听器(在您的情况下是 clone
)。
clone.querySelector('button').addEventListener('click', function(){
//code logic here
});
你可能还想说:
document.registerElement('x-proto-child', {
prototype: ProtoChild
});
这是它的样子:
<!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>
不久前我遇到了类似的问题:
http://www.davevoyles.com/accessing-member-functions-in-polymer/
您是在使用 Polymer,还是只使用 Web Components?
只要将您尝试调用的函数包装在聚合物就绪事件中,就可以开始了,并且可以从您的聚合物元素中调用函数。
Polymer parses element definitions and handles their upgrade asynchronously. If you prematurely fetch the element from the DOM before it has a chance to upgrade, you’ll be working with a plain HTMLElement, instead of your custom element.
或者,您可以根据需要查询自定义元素(通过 ID、class、attr 或元素名称)并获得相同的结果。这是他的例子:http://jsbin.com/qikaya/2/edit