Skip to main content
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: RichardCooper
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: RichardCooper

Is Simulating User-Input Events With jQuery Ever A Good Idea?

By
Published in Comments (24)

The other day, I was having a discussion with Ryan Jeffords about programmatically simulating user-based input events with jQuery. Ryan was in the middle of building a very rich, very dynamic user interface for an eCommerce system's price adjuster module when he hit a bit of snag trying to programmatically simulate a user's click event on a checkbox. As I blogged about a while back, when you simulate a click() event on a checkbox, the checkbox's "checked" status does not actually change until after your event handler has executed. This is very different from a user-triggered checkbox click event in which the checkbox's "checked" status changes prior to the event handler execution.

After discussing the possible work-arounds to this problem, I start to think about simulating user-based input events in general. Checkboxs happen to cause a problem because of their state; but, state aside, is simulating user-based input events ever a good idea? Before we get into that discussion, let's take a quick look at what it means to programmatically simulate a user-based input event.

In the following demo, I have a paragraph of information that the user can show and hide by clicking on certain links:

<!DOCTYPE HTML>
<html>
<head>
	<title>Simulating User-Generated Events In jQuery</title>
	<style type="text/css">

		#more-info {
			background-color: #F0F0F0 ;
			border: 1px solid #CCCCCC ;
			display: none ;
			padding: 15px 15px 15px 15px ;
			width: 400px ;
			}

	</style>
	<script type="text/javascript" src="../jquery-1.4.js"></script>
	<script type="text/javascript">

		// When the DOM is ready, initialize scripts.
		jQuery(function( $ ){

			// Get references to our links.
			var toggleLink = $( "#toggle" );
			var closeLink = $( "#close" );

			// Get a reference to our more-info container.
			var moreInfo = $( "#more-info" );


			// Bind the toggle link so that it toggles the more
			// info box's visibility.
			toggleLink.click(
				function( event ){
					// Cancel default event (navigation).
					event.preventDefault();

					// Slide toggle the more info container.
					moreInfo.slideToggle( 500 );
				}
			);

			// Bind to the close link. Since this will only be
			// available when the more-info is open, we can
			// leverage the toggle link.
			closeLink.click(
				function( event ){
					// Cancel default event (navigation).
					event.preventDefault();

					// Trigger the click event on the toggle link.
					// This will simulate the user clicking on the
					// toggle link directly.
					toggleLink.click();
				}
			);

		});

	</script>
</head>
<body>

	<h1>
		Simulating User-Generated Events In jQuery
	</h1>

	<p>
		<a id="toggle" href="#">Click here</a> for more info.
	</p>

	<p id="more-info">
		Wow! Did you see Joanna yesterday? Should it even be
		allowed for someone to be looking that good? It seems
		rather unfair to the rest of us who are trying to
		concentrate.... <a id="close" href="#">Close</a>
	</p>

</body>
</html>

As you can see in the above code, the paragraph of information starts off closed and the page has a primary "Toggle" link. If the user clicks this toggle link, it will show the paragraph if it is hidden and hide it if it is visible. Once the paragraph is made visible, the user has the option to click another "Close" link contained within the paragraph. This close link simply turns around and programmatically triggers a click() event on the Toggle link. Since the close link is only available when the paragraph is open, triggering a click() event on the toggle link will successfully close the paragraph.

At first glance, this looks really awesome; we're centralizing are client-side business logic in a single method (the toggle link event handler) and then we're invoking that business logic in several different places. It appears that we're keeping our code DRY (Don't Repeat Yourself) and making good use of code reuse. But, is that really what we're doing? Let's take a step back and think about "intent."

Every time a user interacts with the browser, the user is acting on some internal intent; they click a button or a link because they believe that the button or link click will precipitate some desired response. In that case, it is the browser's responsibility to translate the user's intent into some action. With intent in mind, let's think about the first demo - when the user clicked the "Close" button, did they intend for the paragraph to "toggle", or to "close?"

Clearly, because the "close" link is labelled "close", the intent of the user was to close the paragraph; it was not to "toggle" the paragraph as that would imply some possibility that the close button would ever do anything other than close the paragraph. That said, does it make sense that our Close link simulates a click() event on the Toggle link? It does by coincidence, but certainly not by intent.

So the real question becomes, do you want your code to work by coincidence? Or by intentful design?

The more I think about it, the more it becomes clear that the role of the event handler is simply to translate the intent of the user-based input event into calls made against the core API of the current page or piece of software.

jQuery Event Handlers Should Only Act As Translators Between The User's Intent And The Underlying API.

This has more of a benefit that pure personal satisfaction; when you code in this manner, your software becomes less susceptible to bugs. Taking the first demo again, imagine that this page had been live for a while and then needed to be updated. A programmer comes along and sees the "Click here for more info" link and decides that that link shouldn't toggle the paragraph, it should only open it since the language on the page makes no mention of "toggle". As such, he changes the toggle link event handler to simply open the paragraph. At this point, our page breaks because the Close link piggybacked by the toggling nature of the, now-altered "click here" link.

If the event handlers' only action had been to translate the intent of the user into API-based actions, then altering the Toggle link would not have caused a problem.

With this new outlook in mind, let's factor the "intent" out of the event handlers and leave the event handlers as mere translators of said intent.

<!DOCTYPE HTML>
<html>
<head>
	<title>Simulating User-Generated Events In jQuery</title>
	<style type="text/css">

		#more-info {
			background-color: #F0F0F0 ;
			border: 1px solid #CCCCCC ;
			display: none ;
			padding: 15px 15px 15px 15px ;
			width: 400px ;
			}

	</style>
	<script type="text/javascript" src="../jquery-1.4.js"></script>
	<script type="text/javascript">

		// When the DOM is ready, initialize scripts.
		jQuery(function( $ ){

			// Get references to our links.
			var toggleLink = $( "#toggle" );
			var closeLink = $( "#close" );

			// Get a reference to our more-info container.
			var moreInfo = $( "#more-info" );


			// ---------------------------------------------- //
			// ---------------------------------------------- //

			// These functions will act as our very simple
			// page API.


			// I open the more info container.
			var openMoreInfo = function(){
				moreInfo.slideDown( 500 );
			};

			// I close the more info container.
			var closeMoreInfo = function(){
				moreInfo.slideUp( 150 );
			};


			// ---------------------------------------------- //
			// ---------------------------------------------- //


			// Bind the toggle link so that it toggles the more
			// info box's visibility.
			toggleLink.click(
				function( event ){
					// Cancel default event (navigation).
					event.preventDefault();

					// Check to see if the container is currently
					// open. If it is, close container, otherwise
					// open it.
					if (moreInfo.is( ":visible" )){

						// Close it.
						closeMoreInfo();

					} else {

						// Open it.
						openMoreInfo();

					}
				}
			);

			// Bind to the close link.
			closeLink.click(
				function( event ){
					// Cancel default event (navigation).
					event.preventDefault();

					// Close the more info container.
					closeMoreInfo();
				}
			);

		});

	</script>
</head>
<body>

	<h1>
		Simulating User-Generated Events In jQuery (v2)
	</h1>

	<p>
		<a id="toggle" href="#">Click here</a> for more info.
	</p>

	<p id="more-info">
		Wow! Did you see Joanna yesterday? Should it even be
		allowed for someone to be looking that good? It seems
		rather unfair to the rest of us who are trying to
		concentrate.... <a id="close" href="#">Close</a>
	</p>

</body>
</html>

In the above code, you can see that we took the slide open and close actions and factored them out into API methods, openMoreInfo() and closeMoreInfo(). Our event handlers now act as links between the user's input event and these new API methods. It's a little more code, but in doing this, I believe that our event handlers become both more closely aligned with the user's intent as well as more resistant to changes in the event handler layer.

Simulating user-based input events is an attractive idea in our software because it works; but, when it is done to make something work merely by coincidence, as in our first demo, I believe that this feature is being abused and can quickly come back to bite us (as in the checkbox-click-simulation problem). This makes me question - is there ever a good time to simulate user-based input events (outside of truly simulating a user for testing purposes)? Right now, I can't think of one.

Want to use code from this post? Check out the license.

Reader Comments

10 Comments

I find myself doing it a lot, but it's because I am working in an old system which needs some refactoring to its core. I completely agree that designing an API to avoid simulating user input or triggering the inout events is absolutely ideal.

9 Comments

I agree; simulating user input to change the 'core' is not optimal. In MVC parlance, this would be a dependency of the model on the view. You should be able to invoke model behavior without use of the view. The view should observe the model's state-change and adjust itself accordingly.

1 Comments

I am very thankful that you are explaining everything in a video in your recent jQuery posts since im not good in jQuery so far but this helps me a lot. Definately better than just reading something.

15,848 Comments

@L,

Glad you are liking the videos. I do my best to try and explain in the text everything that I talk about in the video, so people who care to skip it can; but, I think it really adds a nice supplement to the explanation.

4 Comments

I agree that you probably don't want to simulate a click event.

I think the intent idea you describe might be an overstatement, though. If the link text changes from "close" to "toggle" do you have to rethink your API because the user is now thinking about toggling and not closing?

This seems like it's a good use of delegates. You don't have to simulate a click event because you can just call the same event handler from both places.

Here's a small refactor.

// Create a toggle delegate.
var toggleMoreInfo = function(event){
event.preventDefault();
if (moreInfo.is( ":visible" )){
// Close it.
moreInfo.close();
} else {
// Open it.
moreInfo.open();
}
};

// Pass the delegate to any links you want to have that functionality.
toggleLink.click(toggleMoreInfo);
closeLink.click(toggleMoreInfo);

Now, I'm not sure I would want the 'event.preventDefault()' in a generic toggle event, but that's the general idea.

15,848 Comments

@Keith,

I don't think you have to change the core API if you change the link verbiage; but, I think you *might* have to change the contents of the event handlers.

I am not sure I like the event object being referenced in the delegate. To me, the delegate should probably be ignorant of the concept of events. You might want to create a toggleMoreInfo() method in the API, and then have the delete pass off to that (after canceling the default event). At that point, however, I might only use a delegate *if* two events cause the same outcome explicitly.

@David,

Not currently, sorry.

4 Comments

I agree that I don't especially like referencing the preventDefault() method of the event object in the delegate. That's because it changes expected behavior of a link and it's not obvious from the toggle command that it's doing that.

It's clearer if you write "preventDefault()" everywhere that you're preventing the default. It's still code repetition, though. If you had enough links which did this then it might be obvious that you meant to preventDefault every time so you'd expect to put it in the delegate.

In principle there's nothing wrong with using the event object in a delegate. Delegates by definition have the same signature so there's the expectation to use them.

The part I really don't like about this code is that you've hard coded the moreInfo target into the function. It would be better if it was in the event object.

In the example below I've created an ICloseable Interface for any object. It will let you open, close, or toggle any item on a click.

You add the ICloseable interface to the object you want to open and close and then you bind any links you want to work to that object.

The key lines are:

$.extend(moreInfo,ICloseable);

// Bind any links
moreInfo.bind('open',toggleLink);
moreInfo.bind('toggle',closeLink);

Here's how I'd probably code this:
// Get references to our links.
var toggleLink = $( "#toggle" );
var closeLink = $( "#close" );

var moreInfo = $( "#more-info" );

// I open the more info container.
var openInfo = function(event){
obj = event.data.toggleItem;
obj.slideDown( 500 );

};

// I close the more info container.
var closeInfo = function(event){
obj = event.data.toggleItem;
obj.slideUp( 150 );
};

// I close the more info container.
var toggleInfo = function(event){
obj = event.data.toggleItem;
if (obj.is( ":visible" )){
// Close it.
obj.close(event);
} else {
// Open it.
obj.open(event);
}
};

var ICloseable = {
"open" : openInfo,
"close" : closeInfo,
"toggle" : toggleInfo,
"bind" : function(action,obj) {
obj.bind('click',{"toggleItem" : this},this[action]);
}
}

// Attach the ICloseable Interface
$.extend(moreInfo,ICloseable);

// bind any links to the object.
moreInfo.bind('toggle',toggleLink);
moreInfo.bind('toggle',closeLink);

As you can see, I use the event object in the delegates.

I took out the preventDefault() behavior, but if you wanted to add it, you could create a bool variable in the bind event which toggled that behavior pretty easily.

15,848 Comments

@Keith,

That's definitely an interesting approach; but there's something about it that just doesn't sit right with me. You're creating a sort of abstract, target-agnostic open/close mechanism; but, the jQuery wrapper is sort of all ready doing that as well. So, you end up having a general method calling another general method. Seems like doubling up on layers of abstraction, and for what purpose, I am not sure?

Personally, I like the explicit execution of item open/closing. I find that it makes the code much more readable.

I feel like in order to maintain flexability, your approach would require a lot of overloading of the event object itself. For example, let's stay that I have multiple elements that I want to open / close, but they don't necessarily have the same open / close durations. How might you account for that? I assume you would make the "duration" a property of the event data?

At that point, though, it almost feels like the open/close methods aren't doing much more than applying the event... and at that point, I think you'll gain readability by having in-line event bindings that make calls on the API.

Of course, this is just my opinion. Whatever works better for you is the way to go.

4 Comments

"For example, let's stay that I have multiple elements that I want to open / close, but they don't necessarily have the same open / close durations."

I wouldn't overload the event object here. I would probably add the durations to the ICloseable interface. The general idea would be that objects know how to close themselves and you want your links to just call a close method on the object.

The only think I would attach to the event object is the target.

As for readability, the bind event for the interface system is easier to read than the click event in your example, but the rest of it is more complicated.

So, if you have lots of bind/click events then the Interface code would get more and more readable than the other way, I imagine.

It's also easier to pass off to other developers since all they need to know is the bind signature.

Finally, and here's where I use this sort of thing, I run loops where I bind a whole mess of objects. Since they all share an interface I can do something like

foreach (var obj in objList)
{
obj.bind();
}

It doesn't matter what the bind event does for these objects as long as they share the interface.
I admit, it does get pretty complicated in Javascript, though.

15,848 Comments

@Keith,

You make a convincing argument. I think I'm just having trouble visualizing all of the references. I think this is something that I actually need to try out myself before I'll truly understand how it's working.

13 Comments

"Ben - A Good Teacher"

Really I am seeing good teacher by watching at least one video per day.
(Hey! don't make this as commercial online tutorial)

Demos are awesome with crystal clear explanation.
Observing your efforts in making the demos interesting with the beautiful images and even in variable names :)

And all your works are forcing/keeping target me to do lot of work on coldfusion n JQuery.

Thanks alot Ben!

With Best Regards,
Raghuram Reddy
http://raghuramcoldfusion.blogspot.com/

2 Comments

A couple of things. One you embed the initial state of of more-info in the CSS. IMHO, that behavior should be in jQuery:

moreInfo.hide();

It shows that the behavior your toggling and closing is more-info visibility.

The second is minor that after you click on toggle the text doesn't change to reflect you want to display or not display more-info.

15,848 Comments

@Joseph,

It's a very interesting point you bring up about the initial state being stored in the CSS vs. in the jQuery. I get conflicted about this. On one hand, I've heard very persuasive arguments about using CSS to model all states; meaning, show/hide would be executed with add/remove CSS classes.

Then, on the other hand, like what you're saying, the state transitions should be defined by jQuery. This makes a lot of sense, especially when the transitions are animated and / or are part of a progressive enhancement approach.

I'm not sure that either camp is 100% correct; right now, I tend to fall in the middle, feeling that each is good in the right context.

2 Comments

The only reason I brought it up was that when reading ur code, I had to stop and jump to the CSS to find out what the initial state is.

Context-switching problem for me. The context-switch that I'm referring to, is of going from a imperative to declarative to imperative paradigm. That is the only reason why I would include the initial state being set in script versus CSS.

15,848 Comments

@Joseph,

You make a good point. Plus, I think there is something very valid to the mind set that anything manipulated with Javascript should also be defined in Javascript. There are, of course, exceptions to this - but I think it might make us more aware of how we are organizing the code.

2 Comments

Hi Ben, I have an instance where simulating a users click event is quite in order. It's an ajax page where a user can select a language by clicking on an image. But, when the application loads the default language is already set. And, the options available there under is only available if the user clicks on the default language image. The php/html looks like this:

<table width="100%" cellpadding="0" cellspacing="0">
	<tr>
		<td>
		<?php
 
		$languages = tep_get_languages();
 
		if(count($languages) > 1) {
 
	echo tep_image(DIR_WS_IMAGES . 'icon_exclamation.png', IMAGE_ICON_EXCLAMATION).'&nbsp;'.AM_AJAX_SELECT_PREFERED_LANGUAGE.'<br>';
 
			foreach ($languages as $amLanguage) {
//Begin New code set the default language
if ($amLanguage['id'] == AM_DEFAULT_LANGUAGE_ID){
			?>
<!--Bug fixed Original & modified to//-->
			&nbsp;<input id="admin_language" type="image" src="<?php echo DIR_WS_CATALOG_LANGUAGES . $amLanguage['directory'] . '/images/' . $amLanguage['image'];?>" title="<?php echo AM_AJAX_CHANGES;?>"
	<?php echo ($attributeManager->getSelectedLanguage() == $amLanguage['id']) ?
	'style="padding:1px;border:1px solid #006600;" onclick="return false;"' : 'onclick="return amSetInterfaceLanguage(\''.$amLanguage['id'].'\');"';?> >
			<?php
}else{....
//End new code
			?>

So I want jquery to simulate a users click event on the default language with the id="admin_language"

I have tried to get it done, without luck so far.

Could you please help me out?
Sara

15,848 Comments

@Sara,

I am not saying that the browser can't generate the same outcome that the user generated; what I am talking about is the method in which that outcome is reached. So, rather than trying to simulate a user "click" to set a language, you should factor out the logic used to set the language and have the browser call it when it needs to. Then, you can always bind the click to call the same inner function.

2 Comments

O' Ben, that's well above my head. I am no coder, but I do have visions out of users perspective. I guess I have to look deep into the code and try to learn something...
Sara

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel