You are quite correct that it doesn't work with delegate. This is quite possibly due to the reasons given here but that is just a guess. Here are a few suggested workarounds.
Without jQuery
You can make this work using event capturing instead of event bubbling as discussed in this blog entry (For IE support see caveat below).
With the following HTML...
<p id='parent'>
Enter some values:<br/>
<input type='text' id='requiredValue'/>
<input type='text'/>
</p>
<div id='output'>
</div>
...this JavaScript shows the events being caught in the correct locations.
var outputDiv = document.getElementById('output'),
parentDiv = document.getElementById('parent'),
inputDiv = document.getElementById('requiredValue');
parentDiv.addEventListener('focus', function() {
outputDiv.innerHTML = outputDiv.innerHTML + "<p>parent</p>";
}, true);
inputDiv.addEventListener('focus', function() {
outputDiv.innerHTML = outputDiv.innerHTML + "<p>input</p>";
}, true);
Making this work with IE
Event capture doesn't work in IE however in the blog mentioned above we can see that IE supports the focusin and focusout events which do what you want.
It means that with the addition of two more event handlers then you can handle this scenario in all cases:
parentDiv.onfocusin = function() {
outputDiv.innerHTML = outputDiv.innerHTML + "<p>parent</p>";
};
inputDiv.onfocusout = function() {
outputDiv.innerHTML = outputDiv.innerHTML + "<p>input</p>";
};
Using latest version of jQuery
This functionality works as expected using the 'on' function.
.on( events [, selector] [, data], handler(eventObject) )
Specifying a selector in the function parameters for 'on' means that 'on' uses event delegation. From the documentation:
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
For the following HTML
<p id='parent'>
Enter some values:<br/>
<input type='text' class='requiredValue'/>
<input type='text'/>
</p>
the JavaScript code below works as expected
$(document).ready(function() {
var initialText = "Enter a value";
$('.requiredValue').val(initialText);
$('#parent').on('focus', '.requiredValue', function() {
var contents = $(this).val();
if (contents === initialText) {
$(this).val("");
}
});
$('#parent').on('blur', '.requiredValue', function() {
var contents = $(this).val();
if (contents.length == 0) {
$(this).val(initialText);
}
});
});
HTML 5 technique
A better technique that is HTML 5 compliant can be found here. From the blog:
<input type="text" name="first_name" placeholder="Your first name...">
You'll notice that all you need to do is add a placeholder attribute with the generic text of your choice. Nice that you don't need JavaScript to create this effect, huh?
Since placeholder is a new capability, it's important to check for support in the user's browser:
function hasPlaceholderSupport() {
var input = document.createElement('input');
return ('placeholder' in input);
}
If the user's browser doesn't support the placeholder feature, you will need to employ a fallback with MooTools, Dojo, or the JavaScript toolkit of your choice:
/* mootools ftw! */
var firstNameBox = $('first_name'),
message = firstNameBox.get('placeholder');
firstNameBox.addEvents({
focus: function() {
if(firstNameBox.value == message) { searchBox.value = ''; }
},
blur: function() {
if(firstNameBox.value == '') { searchBox.value = message; }
}
});