JSFiddle - React, Tailwind, and code Playground
by Gust van de Wal
June 06, 2019
HTML
<p>I <span class="selectable" options="love|really like|really love">like</span> apples.</p>
<p>There's nothing <span class="selectable" options="in this world|in the universe">on earth</span> I eat more than apples.</p>
CSS
body {
font-family: sans-serif;
}
.selectable {
cursor: default;
}
.selectable:focus {
outline: none;
}
.selectable div {
position: relative;
display: inline-block;
}
.selectable::before,
.selectable::after,
.selectable span {
color: #F00;
}
.selectable.filled::before {
content: '{';
margin-right: .2em;
}
.selectable.filled::after {
content: '}';
margin-left: .2em;
}
.selectable ul {
position: absolute;
top: calc(100% + .2em);
left: calc(-.4em - 1px);
list-style: none;
margin: 0;
padding: 1px 0 0;
overflow: hidden;
background-color: #DDD;
z-index: 1;
}
.selectable ul:not(:focus-within) {
pointer-events: none;
user-select: none;
opacity: 0;
transform: translateY(-5px);
transition: opacity .25s ease, transform .4s ease;
}
.selectable ul:focus-within {
transition: opacity .25s ease, transform .4s ease -.15s;
}
.selectable li {
white-space: nowrap;
padding: .4em;
margin: 0 1px 1px;
background-color: #FFF;
}
.selectable li:hover {
background-color: #F7F7F7;
}
.selectable li.active {
display: none;
}
JavaScript
$(function () {
$('.selectable').each(function () {
var $this = $(this)
var list = $this.attr('options').split('|')
var text = $this.text()
$this
.data('original', text)
.html('<div><span>' + text + '</span><ul><li tabindex="0">' + list.join('</li><li tabindex="0">') + '</li></ul></div>')
}).on('mousedown', function (e) {
e.preventDefault()
var $this = $(this)
var $target = $(e.target)
var $focus = $this.find(':focus')
if ($focus.length) $focus.blur()
else $this.find('li:not(.active)').eq(0).focus()
if ($target.is('li')) changeSelection($this, $target)
}).on('keydown', function (e) {
var which = e.which
if (which === 13) changeSelection($(this))
else if (which === 40) $(this).find(':focus').next().focus()
else if (which === 38) $(this).find(':focus').prev().focus()
})
function changeSelection ($this, $target) {
$target = $target || $this.find(':focus')
$target.blur()
$this
.one('transitionend', function () {
$target.addClass('active').siblings().removeClass('active').last().focus().blur()
})
.addClass('filled')
.find('span').text($this.data('original') + ' | ' + $target.text())
}
})