Adventures with the Clipboard API

Adventures with the Clipboard API

22 Mar 2020 events clipboard javascript

I was recently working on a project where a user was able to enter either plain text or HTML into a texarea, or a mix of either.

A problem soon surfaced whereby if the user copied the HTML from elsewhere then pasted it into the textarea, it may come with unwanted attributes that we didn't want to store. So I decided to try and hack the Clipboard API.

Since it's not possible to modify the clipboard data before it ends up in the field, we'll need to interrupt the paste action via our old friend event.preventDefault() and do things a little more manually.

let el = document.querySelector('textarea'); el.addEventListener('paste', evt => { evt.preventDefault(); });

Great. So we can stopped the show. Now we want to grab the intended paste data with a view to doing our modifications. We grab it via getData() method of the clipboardData object passed to our event.

let data = event.clipboardData.getData('text/plain');

Now for our modifications - in my case, to remove unwanted style, data-* and on* attributes. To do this, we're going to parse it as HTML by inserting it into an off-DOM element we'll create, then iterating over its children (i.e. the parsed HTML tags that were pasted) and stripping off the attributes.

let div = document.createElement('div'); div.innerHTML = data; [...div.children].forEach(el => [...el.attributes].forEach(attr => { if (/^(style|data-|on)$/.test(attr.name)) el.removeAttribute(attr.name) }) );

Great! The only tricky part now is inserting the modified paste data into the textarea at the position the user intended, i.e. at the current cursor point (or replacing the current selection, if any text is selected). We can do this with the Range API.

let [start, end] = [el.selectionStart, el.selectionEnd]; el.setRangeText(div.innerHTML, start, end);

We grab the start and end parts of the current selection (if no text is selected, and the cursor is simply blinking at a specific point, these will be the same), and then use the setRangeText() method, inherited by input/textarea elements, to insert our modified paste data.

And that's it! View/try the demo.