使用Ractive.extend()时如何引用DOM元素?

How to refer to DOM Elements When Using Ractive.extend()?

因此,如果我使用 Ractive.extend() 来实例化自定义 class 的对象,那么我将如何引用 class 中的 DOM 元素?我显然不能像往常一样使用 document.getElementById() 因为 DOM 还没有被渲染并将 Ractive.findComponent() 传递给我的构造函数 returns "Ractive.findComponent is not a function."我想做的是这样的:

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}
});

预计到达时间:这也行不通...

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}
});

而且这也不...

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}
});

更新了对评论中阐明的问题的回答

你这里遇到了一个先有鸡还是先有蛋的小问题:

{{#each sprites}}
    <canvas id={{.id}}></canvas>
{{/each}}

意味着 Ractive 将为 sprites 中的每个项目创建一个 <canvas> 并为其分配适当的 ID。但是,sprites 在初始渲染时未定义,因此不会创建 <canvas> 元素。

然后,在 onrender() 中创建 class 的实例,它假定 <canvas> 元素已经存在。只有在那之后你将实例添加到 sprites,告诉 Ractive 渲染元素:

this.set({ sprites });

有几种可能的解决方案。 (我认为)最好的方法是将 render() 方法添加到 class 并将任何与 DOM 相关的内容移出构造函数。然后您的代码将更改为如下所示:

onrender () {
    var sprites = this.get('repeaters').map(function(repeater){    
        return new CanvasSprite(repeater.id);
    });

    this.set({ sprites });

    sprites.forEach(function (sprite) {
        sprite.render();
    });
}

但是,如果您不想修改 class,您可以简单地使用现有的 repeaters 数组作为 {{each}} 循环。此处演示:http://jsfiddle.net/9xLzj3zd/1/

重点是,在使用 document.getElementsById() 或类似方法之前,您需要一种方法告诉 Ractive 渲染 <canvas> 元素。

原回答

如果您需要访问 DOM,请将初始化代码从 oninit 移至 onrender。然后您可以使用 document.getElementById()ractive.find() (documentation here) 来获取您想要的元素。

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' },
});

演示:http://jsfiddle.net/Lgpe2h5L/1/

编辑: 完整的 运行 示例位于:http://jsbin.com/vuziyepeku/edit?html,js,output

我会将每个 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);
        });
    }
});

然后在主实例中使用:

var CanvasAnimation = Ractive.extend( {
    template: '#animation',
    components: {
        'canvas-sprite': CanvasSpriteComponent
    }   
});

var canvasAnimation = new CanvasAnimation({
    el: 'main',
    data: { repeaters: repeater }
});

可以说,您的 CanvasSprite class 使用 canvas 节点更有意义,因为它不再与 how 相关联该节点位于(当前 document.getElementById)。

如果您根本不想更改 class,您只需要构建一个唯一的 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);
        });
    }
});