Paste middlewares example

by Comandeer

HTML

<div id="editor" contenteditable></div>

<ul id="images"></ul>

CSS

#editor {
	border: 5px dashed red;
	padding: 10px;
	min-height: 2em;
}
#editor:empty::after {
	content: 'Paste inside me!';
}
#editor:focus-within::after {
	content: '';
}

JavaScript

class PasteHandler {
	constructor() {
		this.handlers = [];
	}

	addHandler( fn ) {
		this.handlers.push( fn );
	}

	handle( evt ) {
		const handlers = this.handlers.slice( 0 );

		evt.preventDefault();
		next();

		function next() {
			const handler = handlers.shift();

			if ( handler ) {
				handler( evt.clipboardData, next );
			}
		}
	}
}

const pasteHandler = new PasteHandler();

pasteHandler.addHandler( ( clipboardData, next ) => {
	console.log( [ ...clipboardData.items ].map( ( { type } ) => type ) );
	next();
} );

pasteHandler.addHandler( ( clipboardData, next ) => {
	const images = document.querySelector( '#images' );

	images.innerHTML = [ ...clipboardData.files ].reduce( ( html, file ) => {
		if ( !file.type.startsWith( 'image') ) {
			return html;
		}

		const url = URL.createObjectURL( file );

		html += `<li><img src="${ url }" alt="${ file.name }"></li>`;

		return html;
	}, '' );

	next();
} );

pasteHandler.addHandler( ( clipboardData ) => {
	const html = clipboardData.getData( 'text/html' );
	const selection = window.getSelection();
	
	if ( !selection.rangeCount ) {
		return false;
	}

	selection.deleteFromDocument();

	const range = selection.getRangeAt( 0 );
	const fragment = range.createContextualFragment( html );

	range.insertNode( fragment );
} );

document.querySelector( '#editor' ).addEventListener( 'paste', pasteHandler.handle.bind( pasteHandler ) );