2

I have a form, and I want to dynamically display certain elements of the form as soon as they are filled out.

Imagine if I had a text input for your name. As you type, I also want your name to appear in a different part of the webpage.

How would I accomplish this in the simplest manner?

AKor
  • 8,550
  • 27
  • 82
  • 136

3 Answers3

6

(See below for an update, I realized later I'd completely forgotten to handle pasting text into the field using a mouse action.)

You can hook the keypress event (you'll probably want keydown and keyup as well) on the text input field and use it to trigger an update of a DOM element elsewhere. For instance:

var nameField = document.getElementById('nameField');
nameField.onkeydown = updateNameDisplay;
nameField.onkeyup = updateNameDisplay;
nameField.onkeypress = updateNameDisplay;
function updateNameDisplay() {
    document.getElementById('nameDisplay').innerHTML = this.value || "??";
}

Live example

That's a very basic example using a DOM0-style event handler (the "onXyz" property, which I don't normally like). The simplest way to do these things is to use a library like jQuery, Prototype, YUI, Closure, or any of several others. They'll smooth over browser differences for you and let you focus on what you're actually trying to do.

Here's the above using jQuery:

$('#nameField').bind('keydown keyup keypress', function() {
    $('#nameDisplay').html(this.value || "??");
});

Live example


Update: Actually, the above will miss things like pasting into the field using the mouse. You'd think a change handler would be good for this, but it doesn't necessarily fire until focus leaves the field, so it's not uncommon to use a timed process like this:

JavaScript, no library:

var nameField = document.getElementById('nameField');
var lastNameValue = undefined;

updateNameDisplay();

setInterval(updateNameDisplay, 100);

function updateNameDisplay() {
  var thisValue = nameField.value || "??";
  if (lastNameValue != thisValue) {
    document.getElementById('nameDisplay').innerHTML = lastNameValue = thisValue;
  }
}

Live example

You'll want to avoid having a separate one of these for each field (instead, use a single timer for all of them) and you'll want to adjust the timer according to what you actually need — be sensitive to the fact that when you're doing this, you're consuming resources. If you'll want to stop the check at some stage (and it's a good idea), save the return value from setInterval:

var timerHandle = setInterval(updateNameDisplay, 100);

...and stop the loop like this:

clearInterval(timerHandle);
timerHandle = 0;

Here's a more complete example of dynamically watching fields, only using the timer when a field has focus. I've used jQuery for this example because it simplifies it and you did ask for simple (if you use another library or don't use any library, you can probably port this easily enough; the main place jQuery is useful in this case is in finding the inputs within the form):

jQuery(function($) {
  var formTimer = 0,
      currentField,
      lastValue;

  updateWatchingIndicator();
  $('#theForm :input')
    .focus(startWatching)
    .blur(stopWatching)
    .keypress(updateCurrentField);

  function startWatching() {
    stopWatching();
    currentField = this;
    lastValue = undefined;
    formTimer = setInterval(updateCurrentField, 100);
    updateWatchingIndicator();
  }

  function stopWatching() {
    if (formTimer != 0) {
      clearInterval(formTimer);
      formTimer = 0;
    }
    currentField = undefined;
    lastValue = undefined;
    updateWatchingIndicator();
  }

  function updateCurrentField() {
    var thisValue;

    if (currentField && currentField.name) {
      thisValue = currentField.value || "??";
      if (thisValue != lastValue) {
        lastValue = thisValue;
        $('#' + currentField.name + 'Display').html(thisValue);
      }
    }
  }

  function updateWatchingIndicator() {
    var msg;

    if (currentField) {
      msg = "(Watching, field = " + currentField.name + ")";
    }
    else {
      msg = "(Not watching)";
    }
    $('#watchingIndicator').html(msg);
  }

});​

Live example

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

Use javascript's onkeypress event. Check this w3schools page about it. You can then use it to call a function that will update the dom element where you want to display the item. Should go something like this:

<script type="text/javascript">
function update(element)
{
  document.getElementById("dynamic_name").innerHTML = element.value;
}
</script>
<input type="text" onkeypress="update(this)" />
<div id="dynamic_name"></div>

You can obviously rename elements and move them around but this is the general concept.

Endophage
  • 21,038
  • 13
  • 59
  • 90
  • 1
    The problem here is that if you paste into the field via the mouse, that's not a keypress. Also, if you only use keypress, the name won't update when you press backspace (at least, not on all browsers). Also wouldn't recommend a global function called `update` (or global functions at all, really, when you can avoid them). – T.J. Crowder Jan 25 '11 at 07:45
  • @T.J. Crowder on the other hand, your suggestion is to use a busy wait tactic. Probably a combination of onkeypress and catching the blur/change events would be better. Polling loops in js is rarely a user friendly solution. – Endophage Jan 25 '11 at 07:54
  • @Endophage: Catching `blur` and `change` won't help with the mouse pasting example (until the user *leaves* the field). A timer loop isn't a busy-wait. A single timer for checking things is quite standard. Heck, some libraries even have built-in features for doing it (Prototype). – T.J. Crowder Jan 25 '11 at 07:59
  • @T.J. Crowder Polling == Busy Wait. While I recognise it is necessary to do this for some things, when there are events you can catch instead you should always go with those. – Endophage Jan 25 '11 at 16:25
  • @Endophage: *"Polling == Busy Wait"* No, it doesn't. They're completely different things. A busy wait is exactly that: Constantly looping without yielding. An interval timer spends nearly all of its time yielded. But that's semantics. What event would you hook to ensure you get proactive notification (not just on blur) when the user right-clicks and chooses "paste" from a context menu? It's not an keyboard event, `change` doesn't fire until you `blur`, it's not a `click`... I'm totally open to hearing about an event that works reliably x-browser in that case (always happy to learn). :-) Best, – T.J. Crowder Jan 25 '11 at 16:32
  • @T.J. Crowder If we're going to assume they will copy/paste then I will concede that the only way to catch that is polling. However, you will also not have any reliable events to indicate to start/stop polling in that situation. And you're right, polling isn't busy wait, I shouldn't write comments before coffee. Either way, I have a great dislike for both polling and busy waiting. – Endophage Jan 25 '11 at 16:56
  • @Endophage: Well, we can start polling on `focus` and stop on `blur` (which is actually what I went back and did after your comments yesterday). `focus` happens when they right-click the field in order to paste, so... :-) – T.J. Crowder Jan 25 '11 at 17:05
  • See [this question](http://stackoverflow.com/q/14194247/109392) for example of a situation where the key events does not fire even though in my head they should. Using Firefox on Android, the key events doesn't fire if word suggestion is turned on in the Android keyboard. It doesn't fire until you press space or some other key that is not part of the keyboard guessing a word. The guessed word from the keyboard is actually sent to the text field, but no event is triggered. – awe Jan 08 '13 at 07:26
0

A very simple and elegant solution could be to use AngularJS:

<input type='text' ng-model='userText'/>
<p ng-bind='userText'></p>
Stefanos Chrs
  • 2,228
  • 3
  • 19
  • 46