Last active
August 14, 2020 04:16
-
-
Save dfkaye/bdaa6bb621154a1366de85c139bf7b23 to your computer and use it in GitHub Desktop.
textarea resize drag handle polyfill for Edge/Internet Explorer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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