Chanpory
Chanpory

Reputation: 3095

Meteor JS: How do I preserve cursor position on contenteditable field between renders?

Problem:

I have several contenteditable elements. When a user focuses on an element, an editing class name is added to the element. I am using Session object to do this, instead of jQuery. The problem is that everytime I focus on the element, it rerenders, and both the cursor position and focus is lost.

I am trying to use the preserve method on the element to prevent it from getting updated when the element is being edited, but cannot get this to work. Any suggestions?

Here's my code:

// Collection
Forms = new Meteor.Collection('forms');

// Fixtures
if (Forms.find().count() === 0) {
  Forms.insert({
    description: "form 1"
  });

  Forms.insert({
    description: "form 2"
  });
}

if (Meteor.isClient) {

  // Helpers
  Template.forms.helpers({
    editState: function() {
      return Session.equals("fieldEditing", this._id) ? "editing" : "";
    },

    forms: function() {
      return Forms.find();
    }
  });

  // Preserve
  Template.forms.preserve(['.editing']);

  // Event handler
  Template.forms.events({
    'focus .form-field' : function (e, template) {
      e.preventDefault();
      Session.set("fieldEditing", this._id);
    }
  });
}

// Template

<head>
  <title>testproject</title>
</head>

<body>
  {{> forms}}
</body>

<template name="forms">
  {{#each forms}}
    <p class="{{editState}} form-field" contenteditable>
      {{description}}
    </p>
  {{/each}}
</template>

Upvotes: 2

Views: 530

Answers (1)

nathan-m
nathan-m

Reputation: 8865

This is caused by Spark not considering the content of the contenteditable node a value that should be kept. Hence each render resets the content of the div, moving the cursor to the beginning.

I found closed issue that is similar https://github.com/meteor/meteor/issues/171 , but I don't think using {{#constant}} regions is a great solution in your case.

I would suggest:

Note, I changed your code to the following to test easier:

if (Meteor.isClient) {    
  // Collection
  Forms = new Meteor.Collection(null);

  // Fixtures
  if (Forms.find().count() === 0) {
    Forms.insert({
      description: "form 1"
    });

    Forms.insert({
      description: "form 2"
    });
  }

  // Helpers
  Template.forms.helpers({
    timer: function(){
      return Session.get("timer");
    },
    forms: function() {
      return Forms.find();
    }
  });

  // Preserve
  Template.forms.preserve({
    ".form-field":function(n){return n.id}
  });

  Session.set("timer",+new Date())
  Meteor.setInterval(function(){
          Session.set("timer",+new Date())
  },2000);

}

// Template

<head>
  <title>testproject</title>
</head>

<body>
  {{> forms}}
</body>

<template name="forms">
  {{timer}}
  {{#each forms}}
    <p class="form-field" contenteditable id="form-{{_id}}">
      {{description}}
    </p>
  {{/each}}
</template>

Upvotes: 1

Related Questions