<button class="btn btn-primary" id="showmodal">Show modal</button>

<div class="container">
  <div class="modal fade" tabindex="-1" role="dialog" id="modal_mesage">
    <form class="form-horizontal">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">Modal title</h4>
          </div>
          <div class="modal-body">
            <div class="form-group">
              <label class="col-sm-2 control-label" for="content">message</label>
              <div class="col-sm-10">
                <textarea id="message" name=message class="form-control" required></textarea>
              </div>
              <div id="menu" class="menu" role="listbox"></div>
            </div>

          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </form>
  </div><!-- /.modal -->

</div>
/*

Forum question answer only:

https://www.sitepoint.com/community/t/how-can-i-position-the-div-to-my-symbol/439301/2



*/
.menu {
  background-color: #f3f3f3;
  position: static;
  clear: both;
  width: 200px;
  margin: 0 0 0 120px;
  height: 0;
  transform: translateY(1rem);
}
@media screen and (max-width: 767px) {
  .menu {
    margin-left: 15px;
  }
}

.menu-item {
  cursor: default;
  padding: 1rem;
  background-color: #eee;
}

.menu-item.selected {
  background-color: slateGray;
  color: white;
}

.menu-item:hover:not(.selected) {
  background-color: #fafafa;
}
$(function(){
  
   $('#showmodal').on('click',function(){
    
      $('#modal_mesage').modal('show');
   });
  
  
    const properties = [
        'direction',
        'boxSizing',
        'width',
        'height',
        'overflowX',
        'overflowY',

        'borderTopWidth',
        'borderRightWidth',
        'borderBottomWidth',
        'borderLeftWidth',
        'borderStyle',

        'paddingTop',
        'paddingRight',
        'paddingBottom',
        'paddingLeft',

        'fontStyle',
        'fontVariant',
        'fontWeight',
        'fontStretch',
        'fontSize',
        'fontSizeAdjust',
        'lineHeight',
        'fontFamily',

        'textAlign',
        'textTransform',
        'textIndent',
        'textDecoration',

        'letterSpacing',
        'wordSpacing',

        'tabSize',
        'MozTabSize',
    ]

    const isFirefox = typeof window !== 'undefined' && window['mozInnerScreenX'] != null


    function getCaretCoordinates(element, position) {
        const div = document.createElement('div')
        document.body.appendChild(div)

        const style = div.style
        const computed = getComputedStyle(element)

        style.whiteSpace = 'pre-wrap'
        style.wordWrap = 'break-word'
        style.position = 'absolute'
        style.visibility = 'hidden'

        properties.forEach(prop => {
            style[prop] = computed[prop]
        })

        if (isFirefox) {
            if (element.scrollHeight > parseInt(computed.height))
                style.overflowY = 'scroll'
        } else {
            style.overflow = 'hidden'
        }

        div.textContent = element.value.substring(0, position)

        const span = document.createElement('span')
        span.textContent = element.value.substring(position) || '.'
        div.appendChild(span)

        const coordinates = {
            top: span.offsetTop + parseInt(computed['borderTopWidth']),
            left: span.offsetLeft + parseInt(computed['borderLeftWidth']),
            // height: parseInt(computed['lineHeight'])
            height: span.offsetHeight
        }

        div.remove()

        return coordinates
    }

    class Mentionify {
        constructor(ref, menuRef, resolveFn, replaceFn, menuItemFn) {
            this.ref = ref
            this.menuRef = menuRef
            this.resolveFn = resolveFn
            this.replaceFn = replaceFn
            this.menuItemFn = menuItemFn
            this.options = []

            this.makeOptions = this.makeOptions.bind(this)
            this.closeMenu = this.closeMenu.bind(this)
            this.selectItem = this.selectItem.bind(this)
            this.onInput = this.onInput.bind(this)
            this.onKeyDown = this.onKeyDown.bind(this)
            this.renderMenu = this.renderMenu.bind(this)

            this.ref.addEventListener('input', this.onInput)
            this.ref.addEventListener('keydown', this.onKeyDown)
        }

        async makeOptions(query) {
            const options = await this.resolveFn(query)
            if (options.lenght !== 0) {
                this.options = options
                this.renderMenu()
            } else {
                this.closeMenu()
            }
        }

        closeMenu() {
            setTimeout(() => {
                this.options = []
                this.left = undefined
                this.top = undefined
                this.triggerIdx = undefined
                this.renderMenu()
            }, 0)
        }

        selectItem(active) {
            return () => {
                const preMention = this.ref.value.substr(0, this.triggerIdx)
                const option = this.options[active]
                const mention = this.replaceFn(option, this.ref.value[this.triggerIdx])
                const postMention = this.ref.value.substr(this.ref.selectionStart)
                const newValue = `${preMention}${mention}${postMention}`
                this.ref.value = newValue
                const caretPosition = this.ref.value.length - postMention.length
                this.ref.setSelectionRange(caretPosition, caretPosition)
                this.closeMenu()
                this.ref.focus()
            }
        }

        onInput(ev) {
            const positionIndex = this.ref.selectionStart
            const textBeforeCaret = this.ref.value.slice(0, positionIndex)
            const tokens = textBeforeCaret.split(/\s/)
            const lastToken = tokens[tokens.length - 1]
            const triggerIdx = textBeforeCaret.endsWith(lastToken)
                ? textBeforeCaret.length - lastToken.length
                : -1
            const maybeTrigger = textBeforeCaret[triggerIdx]
            const keystrokeTriggered = maybeTrigger === '@'

            if (!keystrokeTriggered) {
                this.closeMenu()
                return
            }

            const query = textBeforeCaret.slice(triggerIdx + 1)
            this.makeOptions(query)

            const coords = getCaretCoordinates(this.ref, positionIndex)
            const { top, left } = this.ref.getBoundingClientRect()

            setTimeout(() => {
                this.active = 0
                this.left = window.scrollX  + coords.left + left + this.ref.scrollLeft
                this.top = window.scrollY +  coords.top + top + coords.height - this.ref.scrollTop
                this.triggerIdx = triggerIdx
                this.renderMenu()
            }, 0)
        }

        onKeyDown(ev) {
            let keyCaught = false
            if (this.triggerIdx !== undefined) {
                switch (ev.key) {
                    case 'ArrowDown':
                        this.active = Math.min(this.active + 1, this.options.length - 1)
                        this.renderMenu()
                        keyCaught = true
                        break
                    case 'ArrowUp':
                        this.active = Math.max(this.active - 1, 0)
                        this.renderMenu()
                        keyCaught = true
                        break
                    case 'Enter':
                    case 'Tab':
                        this.selectItem(this.active)()
                        keyCaught = true
                        break
                }
            }

            if (keyCaught) {
                ev.preventDefault()
            }
        }

        renderMenu() {
            if (this.top === undefined) {
                this.menuRef.hidden = true
                return
            }

            this.menuRef.style.left = this.left + 'px'
            this.menuRef.style.top = this.top + 'px'
            this.menuRef.innerHTML = ''

            this.options.forEach((option, idx) => {
                this.menuRef.appendChild(this.menuItemFn(
                    option,
                    this.selectItem(idx),
                    this.active === idx))
            })

            this.menuRef.hidden = false
        }
    }

    const users = [
        { username: 'John Doe' },
       { username: 'John Doe' },
       { username: 'John Doe' },
       { username: 'John Doe' },
       { username: 'John Doe' },
       { username: 'John Doe' },
       { username: 'John Doe' },

    ]

    const resolveFn = prefix => prefix === ''
        ? users
        : users.filter(user => user.username.startsWith(prefix))

    const replaceFn = (user, trigger) => `${trigger}${user.username} `

    const menuItemFn = (user, setItem, selected) => {
        const div = document.createElement('div')
        div.setAttribute('role', 'option')
        div.className = 'menu-item'
        if (selected) {
            div.classList.add('selected')
            div.setAttribute('aria-selected', '')
        }
        div.textContent = user.username
        div.onclick = setItem
        return div
    }

    new Mentionify(
        document.getElementById('message'),
        document.getElementById('menu'),
        resolveFn,
        replaceFn,
        menuItemFn
    )
});
Run Pen

External CSS

  1. https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css

External JavaScript

  1. https://code.jquery.com/jquery-3.7.1.min.js
  2. https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js