The essence of the following, let's say this text is in a div -

привет привет привет привет @привет привет 

how to find out the position from the upper left corner of the div-a, top and left of the "@" sign in pixels?

  • and why is it needed? - Grundy
  • In principle, the repetition of the Facebook implementation of the link to the user, the mention of the user in the commentary - sivik_xes
  • The question has already been discussed on stackoverflow: stackoverflow.com/a/23526970/1941460 , the implementation works without add. wrappers in span . - Pavel Azanov
  • @Pavel Azanov A little bit wrong, a similar implementation has already been done, the question is to implement without moving the carriage, the user has put "@" and writes behind it - "Ivan V" and under "@" an autocomlete appears by coincidence - this is already on fb.com - sivik_xes
  • @sivik_xes, in fact, it is not necessary to move the carriage, when typing @ carriage is in the right place, its coordinates are minus the width of the @ symbol. Something like that - Grundy

3 answers 3

Based on the answer to the English question, you can find out the current position of the cursor, check what you entered when you type, if you enter the required character - get the coordinates of the cursor and show the pop-on.

When you click as well check the coordinates of the cursor and calculate whether to show popup.

In the example below, the popup is shown if the cursor is not further than 5 positions to the right of the @ character.

The example is very simple, and may require modifications for use.

 var popup = document.querySelector('#popup'); function getCurrentRange() { var range = window.getSelection().getRangeAt(0).cloneRange(); range.setStart(range.startContainer, Math.max(0, range.startOffset - 1)); return range; } function showPopupAt(range, distance) { range.setStart(range.startContainer, Math.max(0, range.startOffset - (distance || 0))); range.setEnd(range.startContainer, range.startOffset + 1); var rect = range.getBoundingClientRect(); popup.style.display = 'block'; popup.style.left = rect.left + 'px'; popup.style.top = rect.bottom + 'px'; } function hidePopup() { popup.style.display = 'none'; } function toggleByDistance(range) { var prev = range.startContainer.textContent.lastIndexOf('@', range.startOffset); var distance = prev != -1 ? range.startOffset - prev : -1; if (distance < 0 || distance > 5) { //for example hidePopup(); } else { showPopupAt(range, distance); } } document.querySelector('[contenteditable]').addEventListener('keyup', function(e) { var range = getCurrentRange() if (e.key == '@') { showPopupAt(range); } else { toggleByDistance(range); } }); document.querySelector('[contenteditable]').addEventListener('click', function(e) { toggleByDistance(getCurrentRange()); }); 
 [contenteditable] { border: 1px solid lightgray; width: 200px; height: 200px; word-wrap: break-word; white-space: pre-wrap; } #popup { display: none; position: absolute; border: 1px solid lightgray; box-shadow: 2px 2px 3px lightgray; left: 0; top: 0; background-color: white; } 
 <div contenteditable></div> <div id="popup"> <div>123</div> <div>234</div> <div>345</div> <div>456</div> <div>567</div> </div> 

  • I have slightly different display conditions, but I can nevertheless fix this code to fit my needs, thank you so much! - sivik_xes
  • Beauty, great) - Vasily Barbashev

The solution to the forehead may be wrapping the @ symbol, for example, in a span and taking its offset.

 document.getElementById('btn').addEventListener('click', function() { var editor = document.querySelector("[contenteditable]"); var html = editor.innerHTML; editor.innerHTML = html.replace(/@/, "<span>$&</span>"); document.getElementById('coord').innerHTML = document.querySelector('span').offsetLeft + ' ' + document.querySelector('span').offsetTop; editor.innerHTML = html }); 
 [contenteditable] { position: relative; } 
 <div contenteditable> привет привет привет привет @привет привет </div> <input type="button" id="btn" value="GetCoord" /> <div id="coord"></div> 

  • The span is clearly superfluous, you could just as well put the cursor in front of @, read the position, and then return the cursor to the place, again several actions, but I would like the implementation as easy as possible - sivik_xes
  • @sivik_xes, yes, as an option, it will not work out otherwise, the text is not considered as an independent unit in the script, so it is impossible to find out by a simple query, for example, the coordinates of the third character in the second line in the text of the element. That is, in any case, there will be either calculations, or invisible wrapping and unfolding and getting the coordinates of a specific element, not a character in the text - Grundy
  • in this case, I don’t even know, I don’t believe that it’s impossible, I’ll dig as far as possible. - sivik_xes 1:58 pm
  • @sivik_xes but you have to believe =) - Vasily Barbashev
  • @ Vasily Barbashev is very positive, you need to believe that there is something impossible), still say that you can’t remove the yellow background when you fill in the chrome) - sivik_xes

Alternatively, you can try using Range , which is supported by all modern browsers .

 <div class="content"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequuntur excepturi laudantium nihil officia totam. @Distinctio dolorem earum fugiat hic illum maiores natus perferendis possimus quod similique sint, temporibus voluptas voluptatum? </div> <script> var contentEl = document.querySelector('.content'); var textEl = contentEl.childNodes[0]; var textContent = textEl.textContent; var index = textContent.indexOf('@'); var range = document.createRange(); range.setStart(textEl, index); range.setEnd(textEl, index + 1); var rects = range.getClientRects(); var offset = { top: rects[0].top - contentEl.offsetTop, left: rects[0].left - contentEl.offsetLeft }; console.log(offset); </script>