Navigate the cursor beyond the span within the content that is editable

I am currently developing a tag input system for a template builder. My main focus right now is on assisting the editor in distinguishing between regular text and formatted text. I am structuring it similar to WordPress shortcodes, where a templated element would be enclosed in square brackets like [shortcode]. However, I am facing an issue where the cursor in my div 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>

Answer №1

One way to achieve this is by adding the contenteditable attribute to your span element and setting its value to false. Make sure to do this after populating it with your shortcode value.

Although this method doesn't physically move the cursor, it prevents any text entered after an inserted shortcode from appearing within the styled span. This ensures that the shotcode tags remain uneditable unless specifically targeted for editing.

For example:

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

  // Variable insert selector
  const $selector = $("#variableSelector");
  // Message content editor
  const $body = $("#bodyContent");
  // Message Recorder
  const $recorder = $('#bodyRecorder');

  // Cursor position
  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;
}

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

Edit (see comments):

https://i.stack.imgur.com/WW3LG.jpg

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Switch tabs with JavaScript

I'm encountering an issue with changing tabs using JavaScript and Ajax. The script I have works when not using Ajax but fails to work properly with it. Here is the script: $(document).ready(function(){ $("#mtabs li").click(function() { ...

A flex container containing two divs each with a white-space:nowrap element set at 50% width

How can I create a parent div that contains two child divs, each with a width of 50% that adjusts based on content? The issue arises when one of the child divs has an h3 element with white-space:nowrap;, causing it to exceed the 50% width. I attempted to ...

Error message displaying "Invalid ID attribute in HTML 5 input"

On my website, I have a page with multiple items, each containing an HTML5 input field. Whenever a user changes the value of the input, it triggers an AJAX call to the server. The server then responds with JSON data, including an array of items that have ...

What is the best way to run a series of basic commands in Node.js in sequence

Is there a way to execute 4 bash commands sequentially in nodejs? set +o history sed -i 's/&& !this.peekStartsWith('\/\/')/ /g' dist/vendor.bundle.js sed -i 's/&& !this.peekStartsWith('\/\/ ...

Embracing JQuery for Frontend Development with Node.js

I'm currently enhancing my node.js skills by utilizing express, and I've encountered a situation that is causing me some confusion. Essentially, when a user requests a webpage and the backend sends them the HTML page, how can I incorporate a Java ...

What is the best way to send a variable or query to a Python script from PHP using an Ajax request?

A Python script I have takes parameters or a SQL query from the PHP file, which I am running by calling an Ajax function. The PHP and Ajax call code has been added here. A variable "action" is created to check different cases. While I can execute action = ...

Validate selected checkbox using JavaScript

Is there a way for me to achieve real-time updates when checking and unchecking multiple checkboxes in a list? Here's the code snippet I currently have: window.onload = function () { var input = document.getElementById('listTaxi'); fu ...

What can cause jQuery to override the window.onbeforeunload function at times?

I've encountered a puzzling issue with jQuery version 1.6.x on my webpage. The problem arises with a simple call as shown below: window.onbeforeunload = function() { return 'Are you sure ?'; }; Surprisingly, this code doesn't work bec ...

Troubleshooting the Missing Border Issue in Tailwind Form Fieldset [Code Snippet Included]

I am developing a basic react application with the help of Tailwind css. Scenario 1: Functioning correctly without tailwind: https://codesandbox.io/s/bold-agnesi-y70ue In this example, the tailwind library is not utilized. The simple react app displays ...

Adding content to the parent element immediately after it is generated using jQuery

Is there a way to trigger $(e).append() as soon as the element e is created without using setTimeout()? I find that method inefficient. Are there any DOM events that can detect changes in a subtree of a DOM element? Usually, I would just add the code to t ...

Choosing2 - incorporate a style to a distinct choice

Let's talk about a select element I have: <select id="mySelect"> <option>Volvo</option> <option value="Cat" class="red">Cat</option> <option value="Dog" class="r ...

Is it achievable to create shadows that do not overlap?

When you hover your mouse over the pill-shaped object, it should smoothly move over the circle as desired. However, the shadow of the circle still remains visible creating an unwanted effect. I am looking for a solution where the shadows do not overlap but ...

pretending to make an ajax call using jQuery

To enhance the user experience of file upload, I have implemented a fake ajax request. When the user clicks submit, the form disappears, a loading graphic appears, and after a few seconds, the page refreshes to display the newly uploaded image. However, e ...

Background Image Division (Twitter Bootstrap)

I am facing a challenge while creating a module with a span8 width and varying height. The div contains an image that dictates its height, with text overlaid on the image. My issue lies in preventing part of the image from getting cropped when Bootstrap re ...

Experience a seamless transition to the next section with just one scroll, allowing for a full

I've been attempting to create a smooth scroll effect to move to the next section using Javascript. However, I'm encountering issues with the window's top distance not being calculated correctly. I'm looking to have the full screen div ...

Incorporating TinyMCE into numerous dynamically generated text areas

I am facing an issue with dynamically created textareas. The content in these textareas is generated dynamically. This is how I retrieve the data and create the textareas dynamically: $(document).ready(function(){ $('#btn').click(function(){ ...

Ways to showcase additional content

When I receive a JSON Object containing posts from a WordPress account, it only includes about 15 posts. What steps can I take to retrieve more than that amount? The structure of the JSON data is as follows: { ID: 4164, title: "24 Hour Non-Stop with Ma ...

Comparing React-Highcharts to regular Highcharts

Despite my efforts to find information on this topic through search engines, I have come up empty. So, I am turning to you with my question. Can someone explain the distinction between these two NPM packages: https://www.npmjs.com/package/highcharts htt ...

Utilizing ExpressJS to save uploaded files using the FileReader object from the front-end

Despite researching multiple posts on this topic, I am still unable to successfully upload a file after numerous adjustments. My React form includes a PDF file upload component which looks like this: <Input onChange={(e) => this.handleFileUpload(e) ...

Tips for altering the color of a specific bar in the Material UI BarChart component

I have implemented the following code to generate a Graph: const namesArray = Object.values(tableData).map(item => item.name); const valuesArray = Object.values(tableData).map(item => item.value); return ( <Box> <SimpleCard ti ...