0

When I select the image, I want the image to be inserted into where the cursor is in the Div. I am using IE11 which prevents me from using document.execCommand like insert HTML or insert image.

Here is a JS Fiddle of the script with a hard coded image. The issue is with URL.createObjectURL

https://jsfiddle.net/lvwiseguy/fLkh2by5/9/

$(document).ready(function() {
  $("#getfile").change(function(event) {
    var imageurl = "url(" + URL.createObjectURL(event.target.files[0]) + ")";
    var image = '<p><img src=' + imageurl + '></p>';
    var sel, range, node;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        range = window.getSelection().getRangeAt(0);
        node = range.createContextualFragment(image);
        range.insertNode(node);
      }
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>
Dan Nick
  • 514
  • 1
  • 9
  • 30
  • `Window.getSelection() ` returns anywhere in the window not only `#divbox` – User863 Apr 10 '19 at 13:20
  • @AswinKumar If you insert your cursor in the div it will only go in that Div. I hard coded an image path and the script works fine. The issue is with createobjectURL – Dan Nick Apr 10 '19 at 13:26
  • What if the page has other `textarea` or `contenteditable`? thats not a good right – User863 Apr 10 '19 at 13:33

2 Answers2

1

The image is not showing because this:

var imageurl = "url(" + URL.createObjectURL(event.target.files[0]) + ")";

should be:

var imageurl = URL.createObjectURL(event.target.files[0]);

as you can see in the following demo.

$(document).ready(function() {
  $("#getfile").change(function(event) {
    // Check if a file was selected.
    if (! event.target.files.length) {
      return;
    }
    var imageurl = URL.createObjectURL(event.target.files[0]);
    // For testing purposes, I set the width to 150px.
    var image = '<p><img src=' + imageurl + ' width="150"></p>';
    var sel, range, node;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        range = window.getSelection().getRangeAt(0);
        node = range.createContextualFragment(image);
        range.insertNode(node);
      }
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>

I also added this part, which would prevent errors when no file/image is selected:

if (! event.target.files.length) {
  return;
}

Just a suggestion

For optimal performance and memory usage, MDN recommends that the object URL be explicitly unloaded using URL.revokeObjectURL(). So in the below snippet, I did what MDN recommended based on their example:

$(document).ready(function() {
  $("#getfile").change(function(event) {
    // Check if a file was selected.
    if (! event.target.files.length) {
      return;
    }
    var img = document.createElement('img');
    img.src = URL.createObjectURL(event.target.files[0]);
    img.onload = function(){
      URL.revokeObjectURL(this.src);
      console.log('Object URL unloaded');
    };
    // For testing purposes, I set the width to 150px.
    var image = '<p><img src=' + img.src + ' width="150"></p>';
    var sel, range, node;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        range = window.getSelection().getRangeAt(0);
        node = range.createContextualFragment(image);
        range.insertNode(node);
      }
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>

And another one..

If no selection was made (inside the #divbox area), then the image is appended to the end of that area's content:

$(document).ready(function() {
  $("#getfile").change(function(event) {
    // Check if a file was selected.
    if (! event.target.files.length) {
      return;
    }
    var img = document.createElement('img');
    img.src = URL.createObjectURL(event.target.files[0]);
    img.onload = function(){
      URL.revokeObjectURL(this.src);
      console.log('Object URL unloaded');
    };
    // For testing purposes, I set the width to 150px.
    var image = '<p><img src=' + img.src + ' width="150"></p>';
    var sel, range, node, has_selection;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        range = window.getSelection().getRangeAt(0);
        node = range.createContextualFragment(image);
        range.insertNode(node);
        has_selection = true;
      }
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
      has_selection = true;
    }
    if (! has_selection) {
      document.querySelector('#divbox').innerHTML += image;
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>

UPDATE

In IE 11 the image gets inserted at the end of the text no matter where the cursor is placed.

Apparently, IE 11 "loses" (or resets?) the selection data when the #getfile button is focused.

So one way to overcome that, is by saving the selection range via the mouseleave event of the #divbox element: (the demo below is based on the third demo above)

var _range;
$('#divbox').mouseleave(function(){
  if (window.getSelection) {
    var sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
      _range = window.getSelection().getRangeAt(0);
    }
  }
});

$(document).ready(function() {
  var _range;
  $('#divbox').mouseleave(function(){
    if (window.getSelection) {
      var sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        _range = window.getSelection().getRangeAt(0);
      }
    }
  });
  $("#getfile").change(function(event) {
    // Check if a file was selected.
    if (! event.target.files.length) {
      return;
    }
    var img = document.createElement('img');
    img.src = URL.createObjectURL(event.target.files[0]);
    img.onload = function(){
      URL.revokeObjectURL(this.src);
      console.log('Object URL unloaded');
    };
    // For testing purposes, I set the width to 150px.
    var image = '<p><img src=' + img.src + ' width="150"></p>';
    var node, has_selection;
    if (_range) {
      node = _range.createContextualFragment(image);
      _range.insertNode(node);
      has_selection = true;
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
      has_selection = true;
    }
    if (! has_selection) {
      $('#divbox').append(image);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>

Using $('#getfile').mouseenter(...) works as well, and there could be other options (i.e. workarounds for the IE 11 issue), but the above approach works for me.

Sally CJ
  • 15,362
  • 2
  • 16
  • 34
  • When I click in the div the cursor is between Fox and Jumped, the image is always inserted after the text. I am using IE 11. I don't know that is the issue but this is the only script I have on the page. – Dan Nick Apr 15 '19 at 13:00
  • That's how your script works - i.e. if for example the cursor is after `Fox`, the image is inserted after that text. Were you expecting something else? – Sally CJ Apr 15 '19 at 23:14
  • It works like that in Chrome but not IE 11. In IE 11 the image gets inserted at the end of the text no matter where the cursor is placed. – Dan Nick Apr 16 '19 at 02:24
0

Here is a working example

Note: you have to check the selection is in the desired contenteditable. This prevents the image insertion to wrong textarea or contenteditable

$(document).ready(function() {
  $("#getfile").change(function(event) {
    var imageurl = URL.createObjectURL(event.target.files[0]);
    var image = '<p><img src=' + imageurl + '></p>';
    var sel, range, node;
    
    if (window.getSelection) {
      sel = window.getSelection();
      let divbox = document.getElementById('divbox');
      if (sel.baseNode &&
        sel.baseNode.parentNode == divbox &&
        sel.getRangeAt &&
        sel.rangeCount) {
        range = window.getSelection().getRangeAt(0);
        node = range.createContextualFragment(image);
        range.insertNode(node);
      }
    } else if (document.selection && document.selection.createRange) {
      document.selection.createRange().pasteHTML(image);
    }
    
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="getfile" value="Go!" />
<div id="divbox" contenteditable="true">The Quick Brown Fox Jumped Over The Lazy Dog</div>
User863
  • 19,346
  • 2
  • 17
  • 41
  • Why would you hard code the id of the div? Like you said if you have more than one content editable it would create issues. I updated the question to include a JS Fiddle. – Dan Nick Apr 10 '19 at 14:15
  • https://stackoverflow.com/questions/21777728/ie-11-document-selection-does-not-work – User863 Apr 10 '19 at 15:23