Emoji Input

Кроссбраузерное поле со вставкой смайлов на чистом JS.

by Yuri Moskvin

HTML

<link rel="stylesheet" href="https://web3r.ru/css/embed/fiddle.min.css">
<script src="https://web3r.ru/js/embed/fiddle.min.js"></script>
<main>

  <h3>
  Использование
  </h3>
  <p>
  По умолчанию смайлы добавляются в поле сразу после текста, но  могут вставляться в зависимости от положения каретки.
  </p>
  <p>
   Для получения введённых данных нажмите <kbd>Enter</kbd>, при этом смайлы заменятся на символы, указанные в регулярном выражении метода <code>einput.get()</code>. Результат будет отображен в консоли браузера.
  </p>

  <form action="#">

    <!-- кнопка открытия панели смайлов -->
    <button type="button" id="btn">
      <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
        <path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"
        />
      </svg>
    </button>
    
    <!-- блок для панели ввода -->
    <div id="input-box"></div>
    
    <!-- блок для панели смайлов-->
    <div id="emoji-box"></div>
  </form>
  
</main>

SCSS

// background-color для панели смайлов
$main-color: #f9f9f9;

p:last-of-type {
  margin-bottom: 2.5rem;
}

form {
  display: block;
  position: relative;
  min-height: 7.5rem;
}

// кнопока открытия панели смайлов
#btn {
  float: left;
  width: 1.5rem;
  height: 1.5rem;
  background: transparent;
  color: darken($main-color,15%);
  cursor: pointer;
  
  // иконка
  svg {
    fill: currentColor;
    display: inherit;
    width: 1rem;
    height: 1rem;
    margin-left: .2rem;
    transition: color .5s ease-in, transform .5s linear;
    -webkit-transition: color .5s ease-in, transform .5s linear;
    transform: rotate(0) translateZ(0);
    backface-visibility: hidden;
    will-change: transform;
   }
   
   &:hover { color: darken($main-color,25%); }
   
   &.active {
     svg {
       color: #6f6fd1;
       transform: rotate(720deg) translateZ(0);
     }
   }

}

// обертка для строки
#input-box {
  max-height: 1.5rem;
  height: 1.5rem;
  cursor: text;
  position: relative;
  overflow: hidden;
  padding: 0 .5rem;
  line-height: 1.5rem;
  background-color: $main-color;
  font-weight: 400;
  
  // поле ввода
  #input-box-field {
    
    display: inline-block; // см. https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
    
    &:focus { outline: 0; }
    
    
    position: absolute;
    top: 0;
    left: .5rem;
    right: .5rem;
    white-space: nowrap;
    overflow-x: scroll; // скрытие скроллбара
    min-height: 2.5rem;
    resize: none;
    text-overflow: ellipsis;
    
    // вывод placeholder
    &:not(:empty) ~ #input-box-placeholder { display: none; }
    &:empty { caret-color: darken($main-color,15%); }
    
    user-select: text;
    -webkit-touch-callout: text;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    -o-user-select: text;
    
    // скрытие лишних тегов
    br, p, div { display: none; }

    // смайлики
    .emoji {
      cursor: text;
      vertical-align:...

JavaScript

/**
 * Анонимная самовызывающаяся функция-обертка
 * @param {document} d - получает документ
 */
!function(d) {

  "use strict";

  /**
   * Полифилл для Object.assign()
   */
  Object.assign||Object.defineProperty(Object,"assign",{enumerable:!1,configurable:!0,writable:!0,value:function(e,r){"use strict";if(null==e)throw new TypeError("Cannot convert first argument to object");for(var t=Object(e),n=1;n<arguments.length;n++){var o=arguments[n];if(null!=o)for(var a=Object.keys(Object(o)),c=0,b=a.length;c<b;c++){var i=a[c],l=Object.getOwnPropertyDescriptor(o,i);void 0!==l&&l.enumerable&&(t[i]=o[i])}}return t}});

  /**
   * Переменная необходима для правильной работы форматирования в IE
   * @type {boolean}
   */
  var _onPaste_Strip = false;

  /**
   * Главный объект
   * @type {Object}
   */
  var einput = {};

  /**
   * Shortcut для preventDefault()
   * @param {Event} e - получает событие
   */
  var prevent = function(e) {
    e.preventDefault();
  };

  /**
   * Функция форматирования контента при вставке в поле
   * @param {Event} e - событие
   * @param {Number} maxlength - максимальная длина контента
   */
  einput.format = function(e, maxlength) {
    if (e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
      e.preventDefault();
      var text = e.originalEvent.originalEvent.clipboardData.getData("text/plain");
      if (text.length > maxlength) text = text.substring(0, maxlength);
      d.execCommand("insertText", false, text);
    } else if (e.clipboardData && e.clipboardData.getData) {
      e.preventDefault();
      var text = e.clipboardData.getData("text/plain");
      if (text.length > maxlength) text = text.substring(0, maxlength);
      d.execCommand("insertText", false, text);
    } else if (window.clipboardData && window.clipboardData.getData) {
      if (!_onPaste_Strip) {
        _onPaste_Strip = true;
        e.preventDefault();
        d.execCommand("ms-pasteTextOnly", false);
      }
     ...