首页 > 解决方案 > Move cursor past span on content editable

问题描述

I am creating a tag entering system for a template builder. Right now I am trying to help my editor "know" the difference between regular text and formatted text because right now I am making it so it is similar to WordPress shortcodes where a templated element would be something like [shortcode] But the issue I am running into right now is that my div's cursor includes any text typed after an inserted shortcode into the styled div as well.

$(function() {
  /**
   * Position tracking.
   */
  class PositionTracker {
    constructor(start, end) {
      this.start = start;
      this.end = end;
    }
  }

  /**
   * Variable insert selector
   * @type {jQuery|HTMLElement}
   */
  const $selector = $("#variableSelector");
  /**
   * Message content editor
   * @type {jQuery|HTMLElement}
   */
  const $body = $("#bodyContent");
  /**
   * Message Recorder
   * @type {jQuery|HTMLElement}
   */
  const $recorder = $('#bodyRecorder');


  /**
   * Position of cursor
   * @type {PositionTracker}
   */
  const position = new PositionTracker(0, 0);

  $selector.on('change', function() {
    let content = $body.html();
    let tag = $('<span>', {
      class: 'text-tag',
      text: $(this).val().toString()
    });
    $body.append(tag);
  });
  $body.on('input mousedown mouseup mouseout', function() {
    $recorder.val($(this).html);
    let selection = window.getSelection();
    position.start = selection.anchorOffset;
    position.end = selection.focusOffset;
  });

});
.text-editor {
  background: #fff;
  margin: 1rem 0;
  padding: 10px 8px;
  font: inherit;
  font-size: 1.15em;
  line-height: 1.35;
  color: #000;
  border: solid 1px rgba(6, 26, 45, 0.65);
  box-shadow: inset 2px 2px 6px rgba(4, 24, 39, 0.35);
}

.text-editor:focus {
  outline: none;
}

.text-editor[contenteditable=true]:empty:before {
  color: darkgray;
  content: attr(placeholder);
  pointer-events: none;
  display: block;
  /* For Firefox */
}

.text-editor.body {
  min-height: 170px;
}

.text-tag {
  background: rgba(0, 195, 255, 0.1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
  <input name="content" type="hidden" id="contentRecoreder" required="required">
  <div id="bodyContent" contenteditable="true" class="content-body text-editor" placeholder="Hi [customer-name], this is [my-name] with [my-company]. We can do that job for just [quote-price]. If you have any questions, call or text us at [my-phone]"></div>

  <label>Variables</label>
  <select name="variables" id="variableSelector">
    <option value="empty">(choose one)</option>
    <option value="[customer-name]">Customer Name</option>
    <option value="[my-name]">My Name</option>
    <option value="[my-company]">My Company</option>
    <option value="[my-price" ]>Quote Price</option>
    <option value="[my-phone]">My Phone</option>
  </select>

  <button type="submit">Save</button>
</form>

标签: javascriptjqueryhtmlcss

解决方案


可能有其他方法可以实现这一点 - 但一种简单的方法是将contenteditable属性添加到您的 span 元素并将其值设置为false- 注意这必须在您使用您的shortcode值填充它之后完成。

请注意,虽然这不会“移动光标”,但它确实会阻止在插入的短代码进入样式跨度之后键入的任何文本。当您考虑它时,这可能是您真正想要的 - 否则您的shotcode标签只需点击输入即可编辑。

例如

$(function() {
  /**
   * Position tracking.
   */
  class PositionTracker {
    constructor(start, end) {
      this.start = start;
      this.end = end;
    }
  }

  /**
   * Variable insert selector
   * @type {jQuery|HTMLElement}
   */
  const $selector = $("#variableSelector");
  /**
   * Message content editor
   * @type {jQuery|HTMLElement}
   */
  const $body = $("#bodyContent");
  /**
   * Message Recorder
   * @type {jQuery|HTMLElement}
   */
  const $recorder = $('#bodyRecorder');


  /**
   * Position of cursor
   * @type {PositionTracker}
   */
  const position = new PositionTracker(0, 0);

  $selector.on('change', function() {
    let content = $body.html();
    let tag = $('<span>', {
      class: 'text-tag',
      text: $(this).val().toString()
    });
    // prevent editing of the span content
    tag.attr('contenteditable', false);
    $body.append(tag);
  });
  $body.on('input mousedown mouseup mouseout', function() {
    $recorder.val($(this).html);
    let selection = window.getSelection();
    position.start = selection.anchorOffset;
    position.end = selection.focusOffset;
  });

});
.text-editor {
  background: #fff;
  margin: 1rem 0;
  padding: 10px 8px;
  font: inherit;
  font-size: 1.15em;
  line-height: 1.35;
  color: #000;
  border: solid 1px rgba(6, 26, 45, 0.65);
  box-shadow: inset 2px 2px 6px rgba(4, 24, 39, 0.35);
}

.text-editor:focus {
  outline: none;
}

.text-editor[contenteditable=true]:empty:before {
  color: darkgray;
  content: attr(placeholder);
  pointer-events: none;
  display: block;
  /* For Firefox */
}

.text-editor.body {
  min-height: 170px;
}

.text-tag {
  background: rgba(0, 195, 255, 0.1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
  <input name="content" type="hidden" id="contentRecoreder" required="required">
  <div id="bodyContent" contenteditable="true" class="content-body text-editor" placeholder="Hi [customer-name], this is [my-name] with [my-company]. We can do that job for just [quote-price]. If you have any questions, call or text us at [my-phone]"></div>

  <label>Variables</label>
  <select name="variables" id="variableSelector">
    <option value="empty">(choose one)</option>
    <option value="[customer-name]">Customer Name</option>
    <option value="[my-name]">My Name</option>
    <option value="[my-company]">My Company</option>
    <option value="[my-price" ]>Quote Price</option>
    <option value="[my-phone]">My Phone</option>
  </select>

  <button type="submit">Save</button>
</form>

编辑(见评论):

显示多个标签


推荐阅读