Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active August 14, 2020 04:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/bdaa6bb621154a1366de85c139bf7b23 to your computer and use it in GitHub Desktop.
Save dfkaye/bdaa6bb621154a1366de85c139bf7b23 to your computer and use it in GitHub Desktop.
textarea resize drag handle polyfill for Edge/Internet Explorer
// 2-6 January 2020
// Minimal textarea resize thumb support for Edge/Internet Explorer.
// Inspired by https://github.com/CezaryDanielNowak/css-resize-polyfill
function resize(textarea) {
// True in browsers that support the resize capability on textareas.
if (document.createElement('textarea').style.resize != null) {
console.log('native');
return;
}
if (!textarea instanceof HTMLTextAreaElement) {
throw Error('Invalid argument: must be an instance of HTMLTextAreaElement');
}
/*
* Read custom "resize" attribute with possible values:
* none | both | horizontal | vertical
*
* see https://developer.mozilla.org/en-US/docs/Web/CSS/resize
*/
var axis = textarea.getAttribute('resize');
if (!/both|horizontal|vertical/.test(axis)) {
return;
}
var cursor = axis == 'horizontal' ? 'ew-resize'
: axis == 'vertical' ? 'ns-resize'
: axis == 'both' ? 'se-resize'
: '';
// Get dimensions and set them explicitly on the style attribute (property).
var style = getComputedStyle(textarea);
Object.assign(textarea.style, {
width: parseFloat(style.width) + 'px',
height: parseFloat(style.height) + 'px'
});
// Create the resize handle, position it as right-bottom, and insert it as next adjacent sibling.
var handle = document.createElement('div');
// Style properties borrowed and modified from
// https://github.com/CezaryDanielNowak/css-resize-polyfill
Object.assign(handle.style, {
background: 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALUlEQVR42mNgwAH279//H4QZyFYAAv///5fBqQurCQQVEHQMVjaMAQQc2BQAABXMU79BvB5bAAAAAElFTkSuQmCC") no-repeat bottom center',
border: '2px solid transparent',
marginBottom: '-6px',
//bottom: '0px',
cursor: cursor,
display: 'inline-block',
minHeight: '1em',
position: 'position',
marginLeft: '-22px',
//right: '0px',
width: '1em',
zIndex: textarea.style.zIndex + 1,
//outline: '1px solid red',
padding: '5px'
});
textarea.insertAdjacentElement('afterend', handle);
// mousedown on handle, track coordinates, add mousemove and mouseup events
function mousedown(e) {
// save selection range
var selection = {
end: textarea.selectionEnd,
start: textarea.selectionStart
};
// track last event coordinates.
var x = e.clientX;
var y = e.clientY;
// min dimensions default to 16px
var minWidth = parseFloat(style.minWidth) || 16;
var minHeight = parseFloat(style.minHeight) || 16;
// mousemove, get coordinate diffs and resize textarea
function mousemove(e) {
/*
* Turn off text selection highlighting while tracking mouse movements.
*/
window.getSelection().removeAllRanges();
if (/both|horizontal/.test(axis)) {
var diffX = e.clientX - x;
if (!diffX) { return; }
var width = parseFloat(textarea.style.width) + diffX;
if (width > minWidth) {
textarea.style.width = width + 'px';
x = e.clientX;
}
}
if (/both|vertical/.test(axis)) {
var diffY = e.clientY - y;
if (!diffY) { return };
var height = parseFloat(textarea.style.height) + diffY;
if (height > minHeight) {
textarea.style.height = height + 'px';
y = e.clientY;
}
}
}
function mouseup(e) {
/*
* Remove event listeners.
*/
handle.removeEventListener('mousemove', mousemove);
handle.removeEventListener('touchmove', mousemove);
handle.removeEventListener('mouseup', mouseup);
handle.removeEventListener('touchend', mouseup);
handle.removeEventListener('mouseout', mouseout);
removeBodyEvents();
restoreSelection(selection);
}
function restoreSelection(selection) {
textarea.setSelectionRange(selection.start, selection.end);
}
function mouseout(e) {
/*
* The pointer can move faster than the re-render and slip outside the
* handle's geometry. So, to keep things from stuttering, delegate the
* mousemove and mouseup to the body element until the handle is moused
* over again.
*/
document.body.addEventListener('mousemove', mousemove);
document.body.addEventListener('mouseup', mouseup);
}
function mouseover(e) {
/*
* User has moused back over the handle while the mouse is still down, so
* restore mouse tracking control to the handle (i.e., remove tracking
* from the body element).
*/
removeBodyEvents();
}
function removeBodyEvents() {
document.body.removeEventListener('mousemove', mousemove);
document.body.removeEventListener('mouseup', mouseup);
}
// Register handle events.
handle.addEventListener('mousemove', mousemove);
handle.addEventListener('touchmove', mousemove);
handle.addEventListener('mouseup', mouseup);
handle.addEventListener('touchend', mouseup);
handle.addEventListener('mouseout', mouseout);
handle.addEventListener('mouseover', mouseover);
}
// Register events that initiate resizing by drag handle.
handle.addEventListener('mousedown', mousedown);
handle.addEventListener('touchstart', mousedown);
}
<!DOCTYPE html>
<title>textarea-resize</title>
<style>
textarea { min-width: 8em; min-height: 1.5em; }
</style>
<body>
<main>
<div>
<textarea resize="horizontal">horizontal</textarea>
</div>
<div>
<textarea resize="vertical">vertical</textarea>
</div>
<div>
<textarea resize="both">both</textarea>
</div>
<div>
<textarea resize="none">none</textarea>
</div>
</main>
</body>
<script src="resize-textarea.js"></script>
<script>
document.querySelectorAll('textarea').forEach(function(textarea) {
resize(textarea);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment