Live textarea foreignObject SVG to Canvas

by Isaiah Odhner

HTML

<textarea xmlns="http://www.w3.org/1999/xhtml" style="border: 0; width: 100px; height: 150px; overflow: hidden;">
Here is a paragraph that requires word wrap.
This is a &lt;textarea&gt;.
</textarea>

CSS

textarea,
canvas {
  position: absolute;
  top: 15px;
  left: 15px;
	
	transform: scale(3);
	transform-origin: left top;
}
canvas {
	pointer-events: none;
	z-index: -1;
	image-rendering: crisp-edges;
	image-rendering: pixelated;
	image-rendering: -moz-crisp-edges;
}

textarea {
	color: transparent !important;
  background: transparent !important;
	caret-color: black;
	resize: none;
}
svg {
	opacity: 0;
	pointer-events: none;
}

JavaScript

var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("version", 1.1);
svg.setAttribute("width", 100);
svg.setAttribute("height", 150);
var foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
foreignObject.setAttribute("x", 0);
foreignObject.setAttribute("y", 0);
foreignObject.setAttribute("width", 100);
foreignObject.setAttribute("height", 150);
svg.append(foreignObject);
var edit_textarea = document.querySelector("textarea");
var mirror_textarea = edit_textarea.cloneNode(false);
foreignObject.append(mirror_textarea);

var canvas = document.createElement("canvas");
document.body.append(canvas);
var ctx = canvas.getContext("2d");

canvas.width = 100;
canvas.height = 150;
canvas.style.outline = "5px solid lightgray";

var update = ()=> {
	requestAnimationFrame(()=> {
		edit_textarea.scrollTop = 0; // prevent scrolling edit textarea to keep in sync
	});

	while (mirror_textarea.firstChild) {
		mirror_textarea.removeChild(mirror_textarea.firstChild);
	}
	mirror_textarea.appendChild(document.createTextNode(edit_textarea.value));
	
	var svg_source = new XMLSerializer().serializeToString(svg);
  //var blob = new Blob([svg_source], {type: "image/svg+xml;charset=utf-8"});
  //var blob_url = URL.createObjectURL(blob);
	var data_url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg_source);
	
	var img = new Image();
	img.onload = ()=> {
    //URL.revokeObjectURL(blob_url);
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		ctx.drawImage(img, 0, 0);
		canvas.style.outlineColor = "lime";
		ctx.getImageData(0, 0, 1, 1);
	};
	img.onerror = (e)=> {
    //URL.revokeObjectURL(blob_url);
		canvas.style.outlineColor = "red";
		console.log("failed to load image", e);
	};
	img.src = data_url; // blob_url;
	
};

edit_textarea.addEventListener("input", update);
update();