Bringing the Jquery cursor into focus following the addition of an emoji

Looking to add emojis in a contenteditable div but facing an issue with the cursor placement. Here is a DEMO created on codepen.io. The demo includes a tree emoji example where clicking on the emoji adds it to the #text contenteditable div. However, after adding the emoji, it appears behind the cursor emojis.

How can I make the added emoji appear in front of the cursor? Can anyone help me with this?

$(document).ready(function() {
  $("body").on("paste", "#text", function(e) {
    e.preventDefault();
  });
  $("body").on("keydown", "#text", function(event) {
    //$('.character').text($(this).text().length);
    if ($(this).text().length === 200 && event.keyCode != 8) {
      event.preventDefault();
    }
  });
  // Click to show clicked smiley
  $("body").on("click",".emoji-box", function(){
      var emojiKey = $(this).attr("data-smiley");
      var emojiURL = $(this).attr("data-url"); 
      $("#text").append("<img src="+emojiURL+" class='app-moji'>"); 
      $("#text").focus();
  });
});
html, body {
    width: 100%;
    height: 100%;
    padding: 0px;
    margin: 0px;
    font-family: 'Helvetica Neue', helvetica, arial, sans-serif;
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    -ms-text-size-adjust: 100%;
    -webkit-texts-size-adjust: 100%;
    -webkit-backface-visibility: hidden;
}
.container {
  position:relative;
  width:100%;
  max-width:500px;
  margin:0px auto;
  padding-top:100px;
}
.input {
  position:relative;
  width:100%;
  display:inline-block;
  padding:5px 0px;
}
#text {
  width:100%;
  outline:none;
  border:1px solid #d8dbdf;
  padding:15px;
  border-radius:3px;
  -webkit-border-radius:3px;
  font-weight:300;
  font-size:14px;
  color:#444;  
  line-height: 17px;
  word-wrap: break-word; 
  text-align: left !important;
   
}
#text img { 
  width:25px;
  height:25px;
  vertical-align: middle; 
}
.app-moji {
  margin-left:5px;
  margin-right:5px;
  display: inline-block;
}
[contenteditable=true]:empty:before {
  content: attr(placeholder);
  display: block; /* For Firefox */
  color:#d8dbdf;
}
.button {
  float:right;
  width:100px;
  padding:8px;
  text-align:center;
  background-color:#d8dbdf;
  border-raduis:3px;
  -webkit-border-radius:3px;
  cursor:pointer;
   transition: background-color 1s linear;
    -moz-transition: background-color 1s linear;
    -o-transition: background-color 1s linear;
    -webkit-transition: background-color 1s linear;
}
.button:hover {
  background-color:#000;
  color:#fff;
     
}
div.coloranimation {
  transition: background-color 1s linear;
    -moz-transition: background-color 1s linear;
    -o-transition: background-color 1s linear;
    -webkit-transition: background-color 1s linear;
  background-color:#000000;
}

.ornekgoster {
  position:relative;
  width:100%;
  padding:10px;
}
.ornek {
  position:relative;
  width:100%;
  font-weight:300;
  font-size:13px;
  color:#444;
} 

*{
  box-sizing:border-box;
  -webkit-box-sizing:border-box;
}

.ap-emoji {
  position: relative;
  float: left;
  width: 100%;
  padding-right: 10px;
   overflow:hidden;
 margin-top:20px;
}

.emoji-box {
   position:relative;
   width:29px;
   height:29px;
   float:left;  
   padding:2px;
   cursor:pointer;
}
.emoji-box img {
   width:100%;
   position:relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="input">
    <div id="text" placeholder="Write something..." contenteditable="true"></div>
    <input type="hidden" id="hidden" value=""/>
  </div> 
  <div class="ap-emoji" id="emoji1">
    <div class="emoji-box" data-smiley=":P" data-url="https://cdn.shopify.com/s/files/1/1061/1924/products/Hungry_Emoji_Icon_c20f1808-f3e2-4051-8941-3d157764e8cb.png"><img src="https://cdn.shopify.com/s/files/1/1061/1924/products/Hungry_Emoji_Icon_c20f1808-f3e2-4051-8941-3d157764e8cb.png"></div>
     <div class="emoji-box" data-smiley=":D" data-url="http://www.bigmouthdesign.co.uk/wp-content/uploads/2017/08/Happy-Face.png"><img src="http://www.bigmouthdesign.co.uk/wp-content/uploads/2017/08/Happy-Face.png"></div>
     <div class="emoji-box" data-smiley="<3" data-url="http://clipart.info/images/ccovers/1496184263Heart-Eyes-Emoji-png-transparent-2.png"><img src="http://clipart.info/images/ccovers/1496184263Heart-Eyes-Emoji-png-transparent-2.png"></div>
    </div>
</div>

Answer №1

Utilize the Selection object in this manner

$(document).ready(function() {
  $("body").on("paste", "#text", function(e) {
    e.preventDefault();
  });
  $("body").on("keydown", "#text", function(event) {
    //$('.character').text($(this).text().length);
    if ($(this).text().length === 200 && event.keyCode != 8) {
      event.preventDefault();
    }
  });
  // Click to show clicked smiley
  $("body").on("click", ".emoji-box", function() {
    var emojiKey = $(this).attr("data-smiley");
    var emojiURL = $(this).attr("data-url");
    $("#text").append("<img src=" + emojiURL + " class='app-moji'>");
    $("#text").focus();
    var el = document.getElementById("text");
    placeCaretAtEnd(el);

  });
});

function placeCaretAtEnd(el) {
  el.focus();
  if (typeof window.getSelection != "undefined" &&
    typeof document.createRange != "undefined") {
    var range = document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
  } else if (typeof document.body.createTextRange != "undefined") {
    var textRange = document.body.createTextRange();
    textRange.moveToElementText(el);
    textRange.collapse(false);
    textRange.select();
  }
}
html,
body {
  width: 100%;
  height: 100%;
  padding: 0px;
  margin: 0px;
  font-family: 'Helvetica Neue', helvetica, arial, sans-serif;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  -ms-text-size-adjust: 100%;
  -webkit-texts-size-adjust: 100%;
  -webkit-backface-visibility: hidden;
}

.container {
  position: relative;
  width: 100%;
  max-width: 500px;
  margin: 0px auto;
  padding-top: 100px;
}

.input {
  position: relative;
  width: 100%;
  display: inline-block;
  padding: 5px 0px;
}

#text {
  width: 100%;
  outline: none;
  border: 1px solid #d8dbdf;
  padding: 15px;
  border-radius: 3px;
  -webkit-border-radius: 3px;
  font-weight: 300;
  font-size: 14px;
  color: #444;
  line-height: 17px;
  word-wrap: break-word;
  text-align: left !important;
}

#text img {
  width: 25px;
  height: 25px;
  vertical-align: middle;
}

.app-moji {
  margin-left: 5px;
  margin-right: 5px;
  display: inline-block;
}

[contenteditable=true]:empty:before {
  content: attr(placeholder);
  display: block;
  /* For Firefox */
  color: #d8dbdf;
}

.button {
  float: right;
  width: 100px;
  padding: 8px;
  text-align: center;
  background-color: #d8dbdf;
  border-raduis: 3px;
  -webkit-border-radius: 3px;
  cursor: pointer;
  transition: background-color 1s linear;
  -moz-transition: background-color 1s linear;
  -o-transition: background-color 1s linear;
  -webkit-transition: background-color 1s linear;
}

.button:hover {
  background-color: #000;
  color: #fff;
}

div.coloranimation {
  transition: background-color 1s linear;
  -moz-transition: background-color 1s linear;
  -o-transition: background-color 1s linear;
  -webkit-transition: background-color 1s linear;
  background-color: #000000;
}

.ornekgoster {
  position: relative;
  width: 100%;
  padding: 10px;
}

.ornek {
  position: relative;
  width: 100%;
  font-weight: 300;
  font-size: 13px;
  color: #444;
}

* {
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
}

.ap-emoji {
  position: relative;
  float: left;
  width: 100%;
  padding-right: 10px;
  overflow: hidden;
  margin-top: 20px;
}

.emoji-box {
  position: relative;
  width: 29px;
  height: 29px;
  float: left;
  padding: 2px;
  cursor: pointer;
}

.emoji-box img {
  width: 100%;
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="input">
    <div id="text" placeholder="Enter your text here..." contenteditable="true"></div>
    <input type="hidden" id="hidden" value="" id="txt" />
  </div>

  <div class="ap-emoji" id="emoji1">
    <div class="emoji-box" data-smiley=":P" data-url="https://cdn.shopify.com/s/files/1/1061/1924/products/Hungry_Emoji_Icon_c20f1808-f3e2-4051-8941-3d157764...
    <div class="emoji-box" data-smiley=":D" data-url="http://www.bigmouthdesign.co.uk/wp-content/uploads/2017/08/Happy-Face.png"><img src="http://www....
    <div class="emoji-box" data-smiley="<3" data-url="http://clipart.info/images/ccovers/1496184263Heart-Eyes-Emoji-png-transparent-2.png"><img ...
  </div>
</div>

Answer №2

If you're looking for a way to track the updated caret position after insertions or deletions, it's important to consider that users can manually adjust the caret position even amidst inserted emojis. One effective approach is to dynamically insert HTML content at a specified caret position.

This method can also be extended to handle pasting of HTML content. Simply remove the event.preventDefault() from your paste event listener on the document body.

When dealing with contentEditable elements, it's beneficial to explore solutions provided by respected individuals like TimDown on Stack Overflow. Given the complexity involved in working with ContentEditable, there may be further requirements down the line. Utilizing a robust standard library can significantly streamline your development process and save time.

Take a look at the code snippet below for practical implementation:

$(document).ready(function() {
  $("body").on("paste", "#text", function(e) {
    e.preventDefault();
  });
  $("body").on("keydown", "#text", function(event) {
    if ($(this).text().length === 200 && event.keyCode != 8) {
      event.preventDefault();
    }
  });
  
  $("body").on("click",".emoji-box", function(){
      var emojiKey = $(this).attr("data-smiley");
      var emojiURL = $(this).attr("data-url"); 
      insertHTMLAtCaret("<img src="+emojiURL+" class='app-moji'>")
  });
  
  function insertHTMLAtCaret(html) {
    // Implementation goes here
  }
});
... (CSS and HTML code snippets omitted for brevity)

Answer №3

execCommand()

To easily bind text editor functions to buttons and target specific caret positions or selected areas, the execCommand() method can be utilized.

In this demo:

The callback function allows for inserting emojis, bold text, and italicized text either at the caret position or over a selected area. Emojis are represented in decimal format using:

&#CODE;

This format will directly render as HTML. For more emoji options, visit amp-what.com.


Demo

const edit = document.forms.editor;

const editText = event => {
  const evt = event.type;
  const tgt = event.target;
  const cur = event.currentTarget;
  const ui = cur.elements;

  if (evt === 'click') {
    switch (tgt.id) {
      case 'emoji':
        let icon = ui.picto.value;
        document.execCommand('insertHTML', false, icon);
        break;
      case 'bold':
        document.execCommand('bold', false, null);
        break;
      case 'italic':
        document.execCommand('italic', false, null);
        break;
      default:
        break;
    }
  }
};

edit.onclick = editText;
button,
select {
  display: inline-block;
  width: 1.75rem;
  height: 1.5rem;
  line-height: 1.5rem;
  padding: 0;
  font: inherit;
  cursor: pointer;
  text-align: center;
  vertical-align: middle;
}

select {
  width: 3.25rem;
  text-align: left;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <style>
    html,
    body {
      font: 300 2ch/1.2 'Times';
      color: #333;
    }

    #text {
      outline: none;
      margin: 10px;
      min-height: 200px;
    }
  </style>
</head>

<body>
  <form id='editor'>
    <fieldset id="text" contenteditable="true"></fieldset>
    <fieldset id='panel'>
      <button id='bold' type='button'><b>B</b></button>
      <button id='italic' type='button'><i>I</i></button>
      <select id='picto'>
        <option value='&#11088;'>&#11088;</option>
        <option value='&#127921;'>&#127921;</option>
        <option value='&#128128;'>&#128128;</option>
        <option value='&#128163;'>&#128163;</option>
        <option value='&#129302;'>&#129302;</option>
      </select>
      <button id='emoji' type='button'>&#11088;</button>
    </fieldset>
  </form>
  <script>
    <!--JavaScript goes here-->
  </script>
</body>

</html>

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

Obtaining select options in jQuery by utilizing the $(this) selector

Looking for a way to loop through and log each option available in a select dropdown box, but struggling with how to use $(this). This is the code I have so far: $('select').each(function(){ $(this).find('options').each(function() ...

Is it possible to caution users before refreshing the page, while still allowing them to

I have a script that displays a warning message to the user when they attempt to refresh the page. It works perfectly for that purpose. However, my issue is that this same alert pops up when a user clicks on a button or a link within the website itself. I ...

Is it possible to utilize onclick for various table cells in jQuery?

I have a situation where I need to loop through dates and display table content accordingly. Could someone please assist me in figuring out how to do this? index.html.erb <% @batches.each do |batch| %> <tr> **<td class = "pie" id ...

How can you preselect an item in Vuetify's item group?

When attempting to preselect an item from a vuetify item-group, I discovered that it works with strings but not with objects. The vuetify documentation shows using a string array as the item list for the item-group, which functions correctly. However, whe ...

What steps can I take to stop my browser from displaying the "waiting for MyHostName" message while performing an ajax Post/Get operation?

Whenever I access a website using Chrome, a message appears in the status bar saying "Waiting for MyHost name" along with an Ajax Loader circle in the browser tab. Here is a javascript function that I am working with: function listen_backend_client_reques ...

I can't seem to figure out why I constantly struggle with adding a check mark to a checkbox using

Take a look at the code I've provided below : HTML : <input type="checkbox" name="xyz[1][]" id="sel_44" style="margin:2px;" value="12345" onclick="myClick(this)"> Javascript : <script> $('#sel_44').attr("checked", true); < ...

Using Jquery to create a popup when clicking on a dynamic anchor tag

I have a series of dynamic anchor tags presented below. Each anchor tag is part of a loop. When any of these links are clicked, I want to trigger a jQuery AJAX post request and display the response in a popup div. How can this be achieved? <script la ...

Adjusting the selected state of an HTML/CSS checkbox with JavaScript

After downloading a theme with a tailored switch component that replaces the standard checkbox functionality, I noticed that the 'checked' status of the underlying checkbox does not change upon click or touch events. Here is the HTML structure: ...

Adjusting image size to accommodate responsive column cell using CSS

I've been working on optimizing my mobile website using a responsive column layout. The challenge I'm facing is getting the images within each column to stretch to 100% width while automatically adjusting their height. I experimented with settin ...

Please insert a decimal point and thousand separator into the text field

I'm trying to incorporate thousand separators and decimal points into my text box. Additionally, I have implemented the following directive: .directive('format', function ($filter) { 'use strict'; return { requir ...

Implementing AngularJS table filters on user click

As a newcomer to angularjs, I am attempting to implement a filter on click. The user will select a source and destination, then click on the filter button. The table should display results based on the input. Upon page load, the table should already contai ...

What is the best way for me to make sure the element overflows properly?

How can I make the contents of the <div class="tags> element cause overflow so that one can scroll its contents instead of the element simply extending in height? I've experimented with different combinations of overflow-y: scroll and min- ...

The accordion seems to be stuck in the open position

Working on a website, I encountered a frustrating bug with an accordion feature. When clicking on the arrow, the accordion opens and closes smoothly. However, when attempting to close it by clicking on the title, the accordion bounces instead of closing p ...

retrieve a reply from a PHP script and incorporate it into JavaScript following an AJAX request

I've been working with an ajax call and everything seems to be running smoothly thanks to this script: $(document).ready(function() { $('.sortable').sortable({ stop: function(event, ui) { $(ui.item).effect("highlight"); ...

Sequentially transferring data to users and processing in Node.js

I am currently working on a website using node.js with express and socket.io. The server is set up to send photos and data from every file in a directory to the user using the socket.emit function. However, I have encountered an issue with the code where ...

Disappearance of the second level in a CSS dropdown menu

I am currently working on creating a dropdown menu using only CSS. Check out this example I'm using: http://jsfiddle.net/DEK8q/8/ My next step is to center the menu, so I added position-relative like this: #nav-container { position: rela ...

Creating a responsive grid layout with HTML and CSS

Is there a way to adjust the grid layout so that the second row of blocks align just below the corresponding block above instead of creating an entirely new row? http://jsfiddle.net/AndyMP/wSvrN/ body { margin-left: 0px; margin-top: 0px; marg ...

The error message "Your session has expired" is displayed by the Wysiwyg

The WysiwygPro editor displays a dialog error message stating, "Your editing session has expired. This is usually caused by a long period of inactivity. Please re-load the page," when clicking on any controls such as Image, Media, Emoticon, etc. This iss ...

Converting Axios URL Parameter to Array of Strings in ExpressJS

How to Send a GET Request using axios: await this.client.get('/endpoint', { params: { query: ['max', 'kevin'] } }) This will result in a URL that looks like this: Request GET /endpoint?query[]=max&query[]=kevin Any sugge ...

In my specific scenario, what is the most effective method for retrieving data from an EntityFramework database using JavaScript?

Currently, within my ASP.NET MVC Core2 project, I have a model in the EF database that contains multiple properties: public class SchoolEvents { public long ID { get; set; } [Required] [StringLength(40, ErrorMessage = "Max 40 c ...