[英]How to refer to DOM Elements When Using Ractive.extend()?
So if I'm using Ractive.extend() to instantiate objects of a custom class then how would I go about referring to DOM elements inside that class? 因此,如果我使用Ractive.extend()来实例化自定义类的对象,那么我将如何引用该类中的DOM元素? I obviously can't use document.getElementById() as usual since the DOM hasn't been rendered yet and passing Ractive.findComponent() to my constructor returns "Ractive.findComponent is not a function." 我显然不能像往常一样使用document.getElementById(),因为尚未呈现DOM并且将Ractive.findComponent()传递给我的构造函数返回“Ractive.findComponent不是函数”。 What I want to do is something like this: 我想做的是这样的:
class myClass {
constructor(id /*,...*/) {
this.element = document.getElementById(id); //how do I do this??
};
};
var Extended = Ractive.extend( {
oninit() {
var myObject = new myClass(id /*,...*/);
this.set({myObject});
}
});
var ractiveExtended = new Extended({
el: document.body,
template: '#template',
data: { id: myId}
});
ETA: This doesn't work either... ETA:这也不起作用......
class myClass {
constructor(id /*,...*/) {
this.element = document.getElementById(id);
};
};
var Extended = Ractive.extend( {
onrender() {
var myObject = new myClass(id /*,...*/);
this.set({myObject});
}
});
var ractiveExtended = new Extended({
el: document.body,
template: '#template',
data: { id: myId}
});
And neither does this... 这也不是......
class myClass {
constructor(id /*,...*/) {
this.element = id;
this.element.innerHTML = 'Hello world!';
};
};
var Extended = Ractive.extend( {
onrender() {
var myObject = new myClass(document.getElementById(id) /*,...*/);
this.set({myObject});
}
});
var ractiveExtended = new Extended({
el: document.body,
template: '#template',
data: { id: myId}
});
Updated answer to the problem clarified in the comments 更新了评论中阐明的问题的答案
You've got a little chicken-egg problem here: 你这里有一个鸡蛋问题:
{{#each sprites}}
<canvas id={{.id}}></canvas>
{{/each}}
means Ractive will create a <canvas>
for each item in sprites
and assign it the appropriate ID. 表示Ractive将为sprites
每个项创建<canvas>
并为其分配适当的ID。 However, sprites
is undefined on initial render, so no <canvas>
elements are created. 但是,初始渲染时未定义sprites
,因此不会创建<canvas>
元素。
Then, inside onrender()
you create instances of your class, which assume the <canvas>
elements are already there. 然后,在onrender()
内部创建类的实例,假设<canvas>
元素已经存在。 Only after that you add the instances to sprites
, telling Ractive to render the elements: 只有在那之后你才将实例添加到sprites
,告诉Ractive渲染元素:
this.set({ sprites });
There are several possible solutions. 有几种可能的解决方案。 The (I think) best would be adding a render()
method to your class and moving anything DOM-related out of constructor. (我认为)最好的方法是在你的类中添加一个render()
方法,并将任何与DOM相关的东西移出构造函数。 Your code would then change to something like this: 然后您的代码将更改为以下内容:
onrender () {
var sprites = this.get('repeaters').map(function(repeater){
return new CanvasSprite(repeater.id);
});
this.set({ sprites });
sprites.forEach(function (sprite) {
sprite.render();
});
}
However, if you don't want to modify your class, you can simply use the existing repeaters
array for the {{each}}
loop. 但是,如果您不想修改类,则只需使用现有的repeaters
数组进行{{each}}
循环。 Demo here: http://jsfiddle.net/9xLzj3zd/1/ 在这里演示: http : //jsfiddle.net/9xLzj3zd/1/
The point is, you need a way to tell Ractive to render the <canvas>
elements before you use document.getElementsById()
or similar methods. 关键是,在使用document.getElementsById()
或类似方法之前,需要一种方法告诉Ractive渲染<canvas>
元素。
Original answer 原始答案
If you need to access the DOM, move the initialization code from oninit
to onrender
. 如果需要访问DOM,请将初始化代码从oninit
移动到onrender
。 Then you can use either document.getElementById()
or ractive.find()
( documentation here ) to get the elements you want. 然后,您可以使用document.getElementById()
或ractive.find()
( 此处的文档 )来获取所需的元素。
class myClass {
constructor(id) {
this.element = document.getElementById(id);
this.element.innerHTML = 'Hello world!';
};
};
var Extended = Ractive.extend( {
onrender() {
// use this.get('id') to get ID from Ractive's data
var myObject = new myClass(this.get('id'));
this.set({myObject});
}
});
var ractiveExtended = new Extended({
el: document.body,
template: '#template',
data: { id: 'content' },
});
Demo: http://jsfiddle.net/Lgpe2h5L/1/ 演示: http : //jsfiddle.net/Lgpe2h5L/1/
EDIT: Full running example available at: http://jsbin.com/vuziyepeku/edit?html,js,output 编辑:完整运行示例: http : //jsbin.com/vuziyepeku/edit?html,js,output
I would encapsulate each CanvasSprite
in it's own component: 我会将每个CanvasSprite
封装在它自己的组件中:
const CanvasSpriteComponent = Ractive.extend({
template: '#sprite',
// Using onrender, not oninit, because we need DOM canvas node
onrender(){
const canvas = this.find('canvas');
const repeater = this.get('repeater');
var sprite = new CanvasSprite(canvas, repeater.width, repeater.height, repeater.spriteSheetURL, repeater.rows, repeater.columns, repeater.totalFrames);
sprite.left = repeater.left; //add variables *not* in the constructor
sprite.setFrame(0);
this.on('setFrame', function (event) {
var offsetY = event.original.clientY - event.node.getBoundingClientRect().top;
var relY = offsetY/sprite.height;
sprite.setFrame(relY);
});
}
});
Then use it in the main instance: 然后在主实例中使用它:
var CanvasAnimation = Ractive.extend( {
template: '#animation',
components: {
'canvas-sprite': CanvasSpriteComponent
}
});
var canvasAnimation = new CanvasAnimation({
el: 'main',
data: { repeaters: repeater }
});
Arguably it makes more sense for your CanvasSprite
class to take a canvas node because then it is no longer tied to how that node is located (currently document.getElementById
). 可以说,它更有意义为您CanvasSprite
类采取帆布节点,因为那是不再依赖于该节点如何定位(目前document.getElementById
)。
If you're not willing to change that class at all, you just need to make up a unique id (or use the index if the repeater collection is not going to mutate). 如果您根本不愿意更改该类,则只需要构成一个唯一的id(如果转发器集合不会变异,则使用索引)。 In this case I'm reusing the component's unique id: 在这种情况下,我重用组件的唯一ID:
const CanvasSpriteComponent = Ractive.extend({
// ...
onrender(){
const canvas = this.find('canvas');
canvas.id = `canvas-${this._guid}`
const repeater = this.get('repeater');
var sprite = new CanvasSprite(canvas.id, repeater.width, repeater.height, repeater.spriteSheetURL, repeater.rows, repeater.columns, repeater.totalFrames);
sprite.left = repeater.left; //add variables *not* in the constructor
sprint.setFrame(0);
this.on('setFrame', function (event) {
var offsetY = event.original.clientY - event.node.getBoundingClientRect().top;
var relY = offsetY/sprite.height;
sprite.setFrame(relY);
});
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.