Chet
Chet

Reputation: 19889

How to focus a DOM child element from a Meteor event handler

I have an event handler that sets a session variable to change the content within a DOM element -- in this case a table cell.

'dblclick td.itemName': function (evt) {
  Session.set("editItemName",true);
  evt.currentTarget.children[0].focus();
},


                <td class="itemName">
                {{#unless editItemName}}
                    {{name}} 
                {{else}}
                    <input class="editItemName" type="text" value="{{name}}" style="width:100px;">
                {{/unless}}
                </td>

Pretty straight forward...

However evt.currentTarget.children doesnt work. Once the input takes place of the text, I'd like to make it automatically focus... The meteor docs say that this is a DOM object so its weird that the children function doesnt work...

Thanks

Chet

Upvotes: 2

Views: 3738

Answers (3)

Larry
Larry

Reputation: 1248

As @Chet pointed out, Template.[name].rendered no longer fires whenever a template is updated, but instead, only when the template is first rendered, and only once.

One can pass a callback to Tracker.afterFlush which will fire every time the template is updated. i.e. all reactive updates are processed

Template.myTemplate.events({
  'dblclick td.itemName': function(e, t) {
    Session.set("editItemName",true);

    Tracker.afterFlush(function() {
      this.find('input').focus();
    }.bind(t));
  }
});

Upvotes: 1

steph643
steph643

Reputation: 2460

You are trying to set the focus to a DOM element that has not been rendered yet.

The issue has been bothering me for a while. I have tried to use the autofocus='autofocus' HTML attribute: it has no effect in Firefox, and in Chrome, it seems to only work the first time the element is rendered.

So we need a handler that is called just after the template is rendered, in order to set the focus with javascript. Template.templateName.rendered looks like the way to go, but there is an issue:

What didn't work for me:

<template name="itemName">
    <td class="itemName">
    {{#unless editItemName}}
        {{name}} 
    {{else}}
        <input type="text" value="{{name}}">
    {{/unless}}
    </td>
</template>

Template.itemName.rendered = function()
{
    this.$('input').focus() 
}

When doing this, Template.yourTemplate.rendered seems to fire only the first time you click on the item (you get the focus correctly only once).

What worked for me:

<template name="itemName">
    <td class="itemName">
    {{#unless editItemName}}
        {{name}} 
    {{else}}
        {{> itemNameEdit}}
    {{/unless}}
    </td>
</template>

<template name="itemNameEdit">
    <input type="text" value="{{name}}">
</template>

Template.itemNameEdit.rendered = function()
{
    this.$('input').focus() 
}

Any explanation from a Meteor expert?

Upvotes: 4

Peppe L-G
Peppe L-G

Reputation: 8345

When you double click, and your function runs, you set the session editItemName to true, and then you're trying to give the input-element focus, but the template has not been re-rendered yet, so the input-element hasn't been created (the template will be re-rendered some time after your function returns). In other words: evt.currentTarget.children[0] is not a reference to the input-element.

Possible solution 1

In HTML 5 there's an attribute called autofocus, which you can use (at least I can in Chrome). Just add it to the input-element:

<input autofocus="autofocus" class="editItemName" type="text" value="{{name}}" style="width:100px;">

Possible solution 2

Otherwise you have to focus it with JavaScript when the template been rendered and your input-element exists in it:

Template.yourTemplate.rendered = function(){

    var input = this.find('.editItemName')

    if(input){
        input.focus()
    }

}

Upvotes: 5

Related Questions