Code Snippet — Accessing Clipboard Images with Javascript

December 3rd, 2011 :: Web Development

a clipboard
The system clipboard is a feature that most of us heavy computer users probably couldn’t survive a day without. While it’s mostly used for copying and pasting text, images can also be stored in the clipboard, by for example using the print screen key. Today I want to share with you how you can access these images with a bit of Javascript code. This works in modern versions of Chrome and Firefox, but the technique differs a bit between them.

Here’s the code:

// We start by checking if the browser supports the
// Clipboard object. If not, we need to create a
// contenteditable element that catches all pasted data
if (!window.Clipboard) {
	var pasteCatcher = document.createElement("div");

	// Firefox allows images to be pasted into contenteditable elements
	pasteCatcher.setAttribute("contenteditable", "");

	// We can hide the element and append it to the body,
	pasteCatcher.style.display = "none";
	document.body.appendChild(pasteCatcher);

	// as long as we make sure it is always in focus
	pasteCatcher.focus();
	document.addEventListener("click", function() { pasteCatcher.focus(); });
}
// Add the paste event listener
window.addEventListener("paste", pasteHandler);

/* Handle paste events */
function pasteHandler(e) {
	// We need to check if event.clipboardData is supported (Chrome)
	if (e.clipboardData) {
		// Get the items from the clipboard
		var items = e.clipboardData.items;
		if (items) {
			// Loop through all items, looking for any kind of image
			for (var i = 0; i < items.length; i++) {
				if (items[i].type.indexOf("image") !== -1) {
					// We need to represent the image as a file,
					var blob = items[i].getAsFile();
					// and use a URL or webkitURL (whichever is available to the browser)
					// to create a temporary URL to the object
					var URLObj = window.URL || window.webkitURL;
					var source = URLObj.createObjectURL(blob);

					// The URL can then be used as the source of an image
					createImage(source);
				}
			}
		}
	// If we can't handle clipboard data directly (Firefox),
	// we need to read what was pasted from the contenteditable element
	} else {
		// This is a cheap trick to make sure we read the data
		// AFTER it has been inserted.
		setTimeout(checkInput, 1);
	}
}

/* Parse the input in the paste catcher element */
function checkInput() {
	// Store the pasted content in a variable
	var child = pasteCatcher.childNodes[0];

	// Clear the inner html to make sure we're always
	// getting the latest inserted content
	pasteCatcher.innerHTML = "";

	if (child) {
		// If the user pastes an image, the src attribute
		// will represent the image as a base64 encoded string.
		if (child.tagName === "IMG") {
			createImage(child.src);
		}
	}
}

/* Creates a new image from a given source */
function createImage(source) {
	var pastedImage = new Image();
	pastedImage.onload = function() {
		// You now have the image!
	}
	pastedImage.src = source;
}

With Chrome we can access the image in the clipboard through the clipboardData object, but this can only be done inside a paste event handler. Inside the event handler, we loop through all items and search for an image. If we find one, we create a Blob of the image, and use the URL of it as the image source.

Firefox on the other hand does not have the clipboardData object, but we can work around this. In Firefox, it’s possible to paste images into elements with the contenteditable attribute set. By creating an invisible contenteditable element which always is focused, we can catch everything that the user pastes. So, everytime the user pastes something, we read through the content in the catcher element and check if it was an image. If it is, we simply take the source of that image and clear the element.

That nasty bit of code you see on line 48, setTimeout(checkInput, 1);, is a way to work around the fact that contenteditable elements don’t trigger oninput events in Firefox. Since the paste event triggers before anything is inserted into the element, we set a 1 ms timeout before we read the element’s content. This is enough time for the pasted content to be inserted.

Once we have the image, we can do anything we want with it, e.g append it to the page, draw it onto a canvas, upload it to a server, etc.

Shameless Self Promotion

I have not created a demo specifically for this, but I’m using this technique on one of my sites: PasteShack. PasteShack is an image uploading site that makes screenshot sharing super duper easy, by using the technique above.

  • Murray

    Great job Joel.  

  • Murray

    It’s amazing what you have managed to do with some neat javascript functions.  Just one question – what format is the image when it arrives at the server.  I’m using this system for my customers to paste screen shots when they come across bugs, and I have the image in the “toDataURL” format, so how do I convert this back into a png (using PHP)?

    • JoelBesada

      The “toDataURL” format is a base64 encoded png image. To convert this back into a regular png file, simply use the base64_decode() function in PHP. :)

  • Sachin_shr

    Hi Joel,

    This really is an amazing and cool tool.PasteShack is exactly what I need. Because I am new to jQuery & Ajax, I am struggling to upload the selected image to server. It would be a great help if you could provide me the sample of upload page in PHP.

    Thanks a lotSachin