简体   繁体   中英

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? 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." 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...

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. However, sprites is undefined on initial render, so no <canvas> elements are created.

Then, inside onrender() you create instances of your class, which assume the <canvas> elements are already there. Only after that you add the instances to sprites , telling Ractive to render the elements:

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. 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. Demo here: 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.

Original answer

If you need to access the DOM, move the initialization code from oninit to onrender . Then you can use either document.getElementById() or ractive.find() ( documentation here ) to get the elements you want.

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/

EDIT: Full running example available at: http://jsbin.com/vuziyepeku/edit?html,js,output

I would encapsulate each CanvasSprite in it's own component:

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 ).

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). In this case I'm reusing the component's unique 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);
        });
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM