I wrote a small jQuery plugin which modifies a specified <textarea>
element when invoked, adding a grayed out selection when the element is unfocused.
// jQuery plugin to make textarea selection visible when unfocused.
// Achieved by adding an underlay with same content with <mark> around
// selected text. (Scroll in textarea not supported, resize is.)
$.fn.selectionShadow = function () {
const $input = this
const prop = n => parseInt($input.css(n))
const $wrap = $input.wrap('<div>').parent() // wrapper
.css({
...Object.fromEntries(
'display width height font background resize margin overflowWrap'
.split(' ').map(x => [x, $input.css(x)])),
position: 'relative',
overflow: 'hidden',
border: 0,
padding: ['top', 'right', 'bottom', 'left'].map(
x => prop(`padding-${x}`) + prop(`border-${x}-width`) + 'px'
).join(' '),
})
const $shadow = $('<span>').prependTo($wrap) // shadow-selection
.css({ color: 'transparent' })
$input // input element
.on('focusin', () => $shadow.hide()) // hide shadow if focused
.on('focusout', () => $shadow.show())
.on('select', evt => { // if selection change
const i = evt.target // update shadow
const [x, s, a] = [i.value ?? '', i.selectionStart, i.selectionEnd]
$shadow.html(x.slice(0, s) + '<mark class=selectionShadow>' +
x.slice(s, a) + '</mark>' + x.slice(a))
})
.css({
boxSizing: 'border-box',
position: 'absolute', top: 0, left: 0, bottom: 0, right: 0,
overflow: 'hidden',
display: 'block',
background: 'transparent',
resize: 'none',
margin: 0,
})
$('head').append(
`<style>mark.selectionShadow { background: #0003; color: transparent }`)
}
Invoke the plugin by with $('textarea').selectionShadow()
.
The above works by adding an 'underlay' to the textarea, and making sure that the underlay have the exact same padding, font style, and word wrapping rules, so that the text of the textarea and the underlay will precisely overlap. Whenever selection in updated the underlay is also updated, and the current selection is marked with <mark>
, which is styled with a gray background (text color is set to transparent
in the underlay, so as not to interfere with the antialias edges of the text).
I wrote the above for a web page of mine, but YMMV. Use freely!