Re:question. Given that the following only applies to Firefox, is there a cross-browser solution? or is the Clipboard API too new (2020).
Original question:
In playing with the Clipboard API in Firefox. I was able to copy an image and paste it into a canvas.
const canvas = document.createElement("canvas");
canvas.setAttribute("contenteditable", "true")
document.body.appendChild(canvas);
canvas.style = "border:solid black 1px";
canvas.addEventListener("paste", element_paste(canvas, "canvas"));
function element_paste(element, type) {
switch (type) {
case "image":
return function(e) {
element.src = image_data_transfer(e.clipboardData || window.clipboardData);
}
case "canvas":
return function(e) {
const im = new Image();
im.src = image_data_transfer(e.clipboardData || window.clipboardData);
im.onload = () => {
element.width = im.width;
element.height = im.height;
element.getContext("2d").drawImage(im, 0, 0);
URL.revokeObjectURL(im.src);
delete im;
};
}
default:
return function(e) {
var p = (e.clipboardData || window.clipboardData).getData("text");
var t = document.createTextNode(p);
element.appendChild(t);
}
}
}
function image_data_transfer(e) {
const p = e.items;
try {
return URL
.createObjectURL(Array
.apply(null, Array(p.length))
.map((_, i) => p[i])
.find(e => e.kind == "file" && /image/.test(e.type))
.getAsFile()
);
} catch (e) {
console.log(e);
return "";
}
}
This works exactly as expected for canvas.
If canvas is changed to an img
however; using "image"
as the type in element_paste
which would work if the html element would allow pasting to it.
I'm aware that I could "easily" overly a canvas over the image and it works exactly as expected. My issue with settling for that is ((I'm juggling another object only to serve as a workaround, I might as well use the canvas to save DOM mess.) (it's ugly.))
Is there any flag experimental or otherwise that would allow pasting directly to an img?
const img = document.createElement("img");
const image_paste = element_paste(img,"image");
document.body.appendChild(img);
img.setAttribute("contenteditable","true");
img.addEventListener("paste",image_paste);
img.style="border:solid black 1px;min-width:100px;min-height:100px";
The above is what I would like to work; below is a hack that does.
const canvas = document.createElement("canvas");
const img = document.createElement("img");
const image_paste = element_paste(img,"image");
document.body.appendChild(img);
document.body.appendChild(canvas);
canvas.setAttribute("contenteditable","true");
canvas.addEventListener("paste",image_paste);
canvas.style="border:solid black 1px;";
img.style="border:solid black 1px;min-width:100px;min-height:100px";
If there's a way for this to work without any script, even better!
sources:
This takes a couple different ideas and puts them together: Your clipboard data filter (modified slightly), and uses the FileReader
class to turn it into a dataurl
, which can be applied to an img
object.
Interestingly though... it does not seem to work when the image object itself is selected.
const image = document.getElementById('img')
document.addEventListener('paste', convertToImage)
function convertToImage(e) {
var blob = image_data_transfer(e.clipboardData)
if (blob !== null) {
var reader = new FileReader();
reader.onload = function(event) {
console.log(event.target.result);
image.src = event.target.result
alert('pasted!')
}; // data url!
reader.readAsDataURL(blob);
} else {
console.log("couldn't read image data from clipboard")
}
}
function image_data_transfer(clipboardData) {
const p = clipboardData.items;
try {
return Array
.apply(null, Array(p.length))
.map((_, i) => p[i])
.find(e => e.kind == "file" && /image/.test(e.type))
.getAsFile()
// URL.createObjectURL();
} catch (e) {
console.log(e);
return null;
}
}
img{
width: 300px;
height: 300px;
border: 1px solid;
background: #ccc;
}
<div>
Paste here!
</div>
<img id="img" src="" onpaste="convertToImage">