0

Pasting of images currently quite sucks in javascript API. There's a workaround in which you make the user paste the image in contenteditable div. The browser then creates a <img> element which you can retrieve using .getElementsByTagName method.

But if user pastes files copied from Explorer or similar program, these don't appear in div but can be retrieved using API.

<div id='paste' contenteditable='true'>Paste</div>

So I have one onpaste event which is fully capable of retrieving pasted file (of any file type):

paste.addEventListener('paste', function(event){
  var items = (event.clipboardData || event.originalEvent.clipboardData);

  /**Try to get a file**/
  var files = items.items || items.files;

  if(files.length>0) {
    //Read the file
    applyFile(files[0].getAsFile? files[0].getAsFile():files[0]);
    //Do not input any text
    event.preventDefault();
    event.cancelBubble = true;
    return false;
  }
  //No files = try to paste text in HTML div
});

In fact, the paste event origin is a <input>, so to get the text pasted as HTML, I steal focus from input field before paste event:

//When pasting, focus on the PASTE element
input.addEventListener("keydown", function(e) {
  if(e.keyCode==86&&e.ctrlKey) {
    paste.focus();
    console.log("Focusing the div");
  }
});

Now after Ctrl + V, if there's no file other event called input will eventually dispatch on the div:

paste.addEventListener('input', function(event){
  //An array of pasted images
  var images = this.getElementsByTagName("img");
  if(images.length!=0) {
     //Do something
     ...
  }
  //Clear the div (image references will be lost!)
  this.innerHTML = "";
  //Focus back on the input
  input.focus();

});

As you can see if an image is pasted in the textarea it will be processed. But if not, the paste event will not do anything!

How can I eventually let the paste event happen in the textarea if no images were pasted?

I tried to save the event in variable:

var lastPaste;
paste.addEventListener('paste', function(event){
  lastPast = event;
  ...
}

And then dispatch it:

paste.addEventListener('input', function(event){
   ...
     //Focus back on the input
   input.focus();
   //The paste event now must be dispatched in textarea:
   input.dispatchEvent(lastPaste);
   lastPaste = null;
}

It doesn't do anything. Nothing is pasted.

Community
  • 1
  • 1
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • text should go to _#paste_, just like the images, so use paste.textContent.trim() to get approx the same text that would otherwise drop into the input – dandavis Nov 19 '14 at 00:11
  • I'm not satisfied with *approx*. Also there may be other listeners on checkbox that handle the pasted text. And most inportantly, I want to avoid re-inventing the paste on caret position. – Tomáš Zato Nov 19 '14 at 00:16
  • change _files_ to var files = items.files, and after your file _if_, add another if to handle text: var cd= event.clipboardData, n=n=cd.types.indexOf('text/html'); if(n!=1){ cd.items[n].getAsString(alert.bind(window)); }; – dandavis Nov 19 '14 at 01:04
  • There's `var` statement and it always was there. Also I don't understand how the rest solves the text insert problem... – Tomáš Zato Nov 19 '14 at 01:07

1 Answers1

0

You can retrieve the text content in your "paste" input and save it in case there's no images.

.clipboardData.getData('text/plain')

Also note that .clipboardData.files is not the same thing as .clipboardData.items and naming .clipboardData items is just confusing as they're different structures.

Rick
  • 1,240
  • 14
  • 21