Problem
From what I have gathered from the question is:
A contenteditable
<div>
is being used.
As the user types, if a keyword ("cat") is entered it will be replaced by a new keyword ("dog").
keyup
event fails to facilitate this behavior.
Explination
Keyboard events such as keyup
are too temperamental:
"Focusable elements can vary between browsers, but form elements can always get focus so are reasonable candidates for this event type."
-- jQuery - .keyup()
So the key to receiving events like keyboard events is being able to get focus
. Elements that can get focus
100% the time are form elements (ex. <input>
, <textarea>
, etc.). While keyboard events are ok on form elements, change
and input
events are specialized exclusively for form events.
Solution
Since OP code requires a contenteditable
<div>
and not a <textarea>
, the Demo has:
One div#view
that is contenteditable
One textarea#edit
that is under div#view
with only it's cursor visible.
As the user types into div#view
, textarea#edit
is actually listening for the input
event and div#view
listens for the keyup
event.
When the input
event happens, #edit
immediately sets the text of #view
to that of its own value. On each keyup
event #view
sets the focus
back to #edit
.
So basically, div#view
and textarea#edit
occupy the same space with #view
being up front and #edit
behind #view
. User input is transparent text on #edit
but its cursor is visible (because it's always getting focus
from #view
during keyup
event). #view
gets its text from #edit
.
As a bonus, the user enters any string to change (like "cat") and enters the string to change to (like "dog").
Note: The reason why I went for such a convoluted solution is because focus
of the cursor. Typing without one feels artificial and typing with a cursor that pops back to the beginning is disorienting.
Demo
Details commented in Demo
If you want to render HTML, see the comments in the jQuery section
$(document).ready(function() {
// textarea listens for input event...
$('#edit').on('input', function() {
// Collect values from inputs and textarea
var from = $('#from').val();
var to = $('#to').val();
var value = $(this).val();
/* if the value of input#from is in the value of textarea...
|| indexOf() will return its index number...
|| so if indexOf() doesn't find it then it returns -1
*/
if (value.indexOf(from) !== -1) {
// Using RegExp Object for variable string
var rgx = new RegExp(from, 'g');
// New value of textarea#edit replaced 'from' with 'to'
value = value.replace(rgx, to);
}
// The text of div#view is the new value of #edit
/* Change .text to .html is you want to render HTML but it
|| be disorienting as what is actually typed is not seen.
|| A more feasible solution is to move #edit below view and
|| change #edit color to a visible color.
*/
$('#view').text(value);
// The value of #edit is the new value
$(this).val(value);
});
// #view listens for keydown event...
$('#view').on('keydown', function() {
// Move focus to #edit
$('#edit')[0].focus();
});
});
html,
body {
font: 400 16px/1.3 Consolas;
}
/* Wrap fieldset.main around both textarea and div
|| div will be on top of textarea because they are absolute
*/
.main {
position: relative;
width: 90vw;
border: 0
}
input {
font: inherit;
width: 20ch;
}
/* Only textarea#edit's cursor is visible */
#edit {
border: 0;
outline: 0;
color: transparent;
caret-color: red;
position: absolute;
width: 100%;
font: inherit;
left: 2px;
}
/* Editable div#view is over textarea#edit */
/* textarea#edit will actually get an input event */
/* User will see the div's text that comes from #edit*/
/* The value typed into #edit is transparent so user only sees
div#view's text and textarea#edit's cursor */
#view {
position: absolute;
pointer-events: none;
z-index: 1;
outline: 3px inset grey;
width: 100%;
min-height: 50px;
left: 2px;
}
<!--UI enter target string and new string-->
<fieldset class='ui'>
<legend>Convert String</legend>
<label>From: </label><input id='from'>
<label>To: </label><input id='to'>
</fieldset>
<!--Wrap editable div and textarea in an relative element-->
<fieldset class='main'>
<!--Div and textaera are absolute see CSS-->
<div id="view" contenteditable="true"></div>
<textarea id='edit'></textarea>
</fieldset>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>