0

Need to insert a link (replace highlighted text)

The idea is - select lorem - type a href inside an input - and pressing Enter - set a link.

Problem - highlighted text is not highlighted if inpa has focus.

Any solution?

$('#inpa').on('keypress', function(e){
 if(e.keyCode == 13){
        let a = $('#inpa').val();
        let b = window.getSelection().toString();
        let ht = "<a href = '" + a + "' target = '_blank'>" + b + "</a>";
        document.execCommand('insertHTML', ht);
 }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class='story' id='story'>lorem ipsum</div>
<br>
<input type='text' class='inpa' id='inpa'>
qadenza
  • 9,025
  • 18
  • 73
  • 126
  • not sure how two things can have focus at once... You would be better off to probably remember the selection and than recreate the selection. – epascarello Mar 14 '19 at 16:12
  • @epascarello, simmilar funcionality is present here on SO, when a post is created having a link inside – qadenza Mar 14 '19 at 16:15
  • Yes before element gets focus you store the selection. When they are done, you recreate the selection and run your code. The rangy library makes it easy to do. – epascarello Mar 14 '19 at 16:19
  • @qadenza the functionality you're referencing seems to be `iframe`-based, because it's an entirely different context for selection. I don't think that's possible with JavaScript alone. One option may be to style selections after `blur` and then reselect on `focus`. If this is an option for you, I could try writing an answer for this. – andrewgu Mar 14 '19 at 16:20
  • @andrewgu, pls try, sounds logical, but maybe the problem could be if selection is not on the end or at the top of parent, but somewhere inside – qadenza Mar 14 '19 at 16:22
  • @qadenza as another answer has already been accepted, I'm not going to be attempting or posting further on this thread in the interest of time. I hope you're able to solve your problem! – andrewgu Mar 14 '19 at 17:10

3 Answers3

2

Here is the basic idea on what I was talking about in the comments on remembering the range and recreating it.

// took this function getSelectionHtml from https://stackoverflow.com/questions/4652734/return-html-from-a-user-selected-text
function getSelectionHtml() {
  var html = "";
  if (typeof window.getSelection != "undefined") {
    var sel = window.getSelection();
    if (sel.rangeCount) {
      var container = document.createElement("div");
      for (var i = 0, len = sel.rangeCount; i < len; ++i) {
        container.appendChild(sel.getRangeAt(i).cloneContents());
      }
      html = container.innerHTML;
    }
  } else if (typeof document.selection != "undefined") {
    if (document.selection.type == "Text") {
      html = document.selection.createRange().htmlText;
    }
  }
  return html;
}


// this gets the selection, and holds on to it. It returns a function if it has a range that will recreate the selection
const selectionSaveRestore = () => {
  let range
  let sel
  if (window.getSelection) {
    sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
      range = sel.getRangeAt(0);
    }
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
  }
  if (range) {
    return () => {
      if (window.getSelection) {
        sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
      } else if (document.selection && range.select) {
        range.select();
      }
    }
  }
  return null;
}

let recreateRange
const tb = document.querySelector("#test");
tb.addEventListener("focus", () => {
  recreateRange = selectionSaveRestore()
})

tb.addEventListener("keypress", evt => {
  if (evt.which === 13 && recreateRange) {
    recreateRange()
    const a = evt.target.value;
    const b = getSelectionHtml()
    const ht = "<a href='" + a + "' target='_blank'>" + b + "</a>"
    document.execCommand('insertHTML', false, ht);
  }
})
<div id="foo" contenteditable="true">
  Bacon ipsum dolor amet leberkas spare ribs swine, tenderloin t-bone pork chop corned beef flank filet mignon rump beef sausage turducken. Pork chop kielbasa ground round, t-bone pork belly turducken brisket ribeye strip steak frankfurter pork loin pastrami
  filet mignon prosciutto bacon. Venison pork kielbasa, doner short ribs ball tip tri-tip porchetta boudin chuck salami shoulder shank shankle pastrami. Pig sirloin strip steak, frankfurter chicken beef ribs ribeye salami t-bone. Pork belly doner short
  loin ribeye burgdoggen ground round pork kielbasa frankfurter. Capicola spare ribs biltong kielbasa rump pork chop cow prosciutto shankle burgdoggen. Turkey shankle pancetta pig.
</div>

<input type="text" id="test" />
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • thanks a lot for your efforts, If this is the only way (seems so) I'm suprised that there is no an easy way for such a trivial task. – qadenza Mar 14 '19 at 16:52
1

To achieve expected result, use below option

  1. Assign selected text to variable
  2. Form a tag with selected text
  3. Replace selected text with a tag from step 2

var text;

$('#story').on('click', function(){
   text = ""
    if (window.getSelection) {
        text = window.getSelection().toString();
    } else if (document.selection && document.selection.type != "Control") {
        text = document.selection.createRange().text;
    }   
});


    $('#inpa').on('keypress', function(e){
      console.log(text)
 if(e.keyCode == 13){
        let a = $('#inpa').val();
        let ht = "<a href = '" + a + "' target = '_blank'>" + text + "</a>";
        let divText = $('#story').text();
        divText = divText.replace(text, ht);
    console.log(ht, text)
        $('#story').html(divText)
        
 }
});   
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class='story' id='story'>lorem ipsum</div>
<br>
<input type='text' class='inpa' id='inpa'>

codepen - https://codepen.io/nagasai/pen/MxQXgJ

Naga Sai A
  • 10,771
  • 1
  • 21
  • 40
0

What you described is impossible if you expect the system to do this for you. However, you can easily achieve this with little JavaScript. When a user selects a text, grab it using JS and highlight it using CSS. I would wrap the text in a span or a div or something and give it a unique ID so you can access it later. You can find out how to get the text here. So when the user focuses on the input, the text will seem like it is still highlighted and once the user sets the link, you can update the text again in whatever way you'd like.

RisingSun
  • 1,693
  • 27
  • 45