Async Clipboard API Demo
HTML
<h1>Async Clipboard API Image Demo</h1>
<section>
<button id="copy"><strong>Copy Chrome Logo</strong><br><em>(write to clipboard)</em></button>
<button id="paste"><strong>Paste Image on Clipboard</strong><br><em>(read from clipboard)</em></button>
</section>
<h3>Permissions:</h3>
<section id="permbuttons"></section>
<h3>Image on clipboard:</h3>
<image id='image-field' width="42" height="42"></image>
<h3>Example 'image/png' images to copy:</h3>
<div>
<image src="https://upload.wikimedia.org/wikipedia/commons/5/53/Google_Chrome_Material_Icon-450x450.png" width="42" height="42"></image>
<image src="https://upload.wikimedia.org/wikipedia/commons/f/f3/Chromium_Material_Icon.png" width="42" height="42"></image>
</div>
<div id="toast" hidden></div>
SCSS
html, body {
font: 16px/1.3 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 300;
height: 100%;
padding: 0;
margin: 0;
background: #eee;
overflow: hidden;
text-align: center;
}
h1 {
margin: 10px;
font: inherit;
font-size: 200%;
color: #634884;
text-shadow: 0 1px 0 #fff;
}
h3 {
margin: 0;
font: inherit;
font-size: 140%;
color: #666;
text-shadow: 0 1px 0 #fff;
}
strong {
font-weight: 500;
}
em {
font-size: 80%;
}
section {
margin: 5px auto;
max-width: 500px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-around;
}
section > * {
flex: 0 1 45%;
}
button {
position: relative;
box-sizing: border-box;
margin: 5px;
padding: 10px 20px;
border: 2px solid #230844;
background: #634884;
border-radius: 5px;
color: #fff;
text-shadow: 0 -1px 0 #000;
cursor: pointer;
font: inherit;
line-height: 1.1;
outline: none;
white-space: nowrap;
}
button:hover, button:focus {
background: #8368A4;
}
textarea {
box-sizing: border-box;
display: block;
max-width: 478px;
margin: 10px auto;
padding: 10px;
font: inherit;
color: #555;
border: 2px solid #432864;
border-radius: 5px;
width: calc(100% - 22px);
height: 6em;
outline: none;
}
#perms {
display: inline-block;
text-align: left;
color: #444;
}
#permbuttons button {
text-align: left;
padding-left: 40px;
&:before {
position: absolute;
content: '\1f47b';
left: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 130%;
text-shadow: 0 0 1px rgba(0,0,0,0.5);
}
&[data-state="granted"] {
background: #495;
border-color: #051;
&:before {
content: '\2705';
}
}
&[data-state="denied"] {
background: #945;
border-color: #501;
&:before {
content: '\1f6ab';
}
}
}
#toast, #toast[hidden] {
display: block;
position:...
JavaScript
async function loadBlob(fileName) {
const fetched = await fetch(fileName);
return await fetched.blob();
}
/** Write the Chrome logo to the clipboard when clicking "Copy" */
copy.onclick = async () => {
try {
const url = 'https://upload.wikimedia.org/wikipedia/commons/5/53/Google_Chrome_Material_Icon-450x450.png';
const blobInput = await loadBlob(url);
const clipboardItemInput = new ClipboardItem({'image/png' : blobInput});
await navigator.clipboard.write([clipboardItemInput]);
log('Image copied.');
} catch(e) {
log(e);
}
};
/** Read from clipboard when clicking the Paste button */
paste.onclick = async () => {
try {
const clipboardItems = await navigator.clipboard.read();
console.log(clipboardItems);
const blobOutput = await clipboardItems[0].getType('image/png');
document.getElementById('image-field').src =
window.URL.createObjectURL(blobOutput);
log('Image pasted.');
} catch(e) {
log('Failed to read clipboard');
}
};
/** Watch for pastes */
navigator.clipboard.addEventListener('clipboardchange', async e => {
const text = await navigator.clipboard.getText();
log('Updated clipboard contents: '+text)
});
/** The 4 available permissions for Async Clipboard API: */
const PERMISSIONS = [
{ name: "clipboard-read" },
{ name: "clipboard-write" }
//{ name: "clipboard-read", allowWithoutGesture: false },
//{ name: "clipboard-read", allowWithoutGesture: true },
//{ name: "clipboard-write", allowWithoutGesture: false },
//{ name: "clipboard-write", allowWithoutGesture: true }
];
/** Query for each permission's state, then watch for changes and update buttons accordingly: */
Promise.all(
PERMISSIONS.map( descriptor => navigator.permissions.query(descriptor) )
).then( permissions => {
permissions.forEach( (status, index) => {
let descriptor = PERMISSIONS[index],
name = permissionName(descriptor),
btn = document.createElement('button');
btn.title = 'Click to request...