How to write a smart onblur event - javascript

Lets imagine we have this sample code:
<input type="text" onblur="blurHandler()" />
<div class="results">
<ul>
<li>sampleText</li>
</ul>
</div>
Lets say you have currently focus on your input tag, and you hover to the "a" tag and click it. The browser will handle the onblur event first.
The task of the blur event is that it should hide the results div, but you still want to be able to click the link in the results div before that happens.

In blurHandler, use setTimeout() to delay hiding your div.
function blurHandler() {
setTimeout(function () {
//close the div
}, 100);
//do whatever else needs to be done
}
jsFiddle Demo
Another option would be to play with the mouseenter/mouseleave events on the link, and use a common flag between the event handlers so that they know about each other.
And one more: you can hide the div with a short animation, so it is actually still there when the click happens. Something like this:
$('.results').hide(1000);
jsFiddle Demo
Note: you should take a look at advanced event handling, inline event handlers can really mess up your HTML quickly. Separation of concerns helps others and your future self. If you use jQuery (seeing the tags under your question), you should use jQuery's event handling methods, which use the advanced model already.

I agree with Marcell's comment, though perhaps more from a usability perspective.
Assigning a timeout (as suggested by bažmegakapa) means you're choosing an arbitrary time limit that may or may not fire before the user has processed what they are supposed to do before that time limit is over. Unless your UI somehow makes it clear that they must react within a given time frame, this is likely to lead to frustrated users.
Even taking for granted that the users have had time to process the directions on screen, there's also transition time between moving from keyboard to mouse (or touch, where it's even worse as you have to deal with the UI shifting to hide the soft-keyboard), which means there's even more variance between different users' and their ability to follow the directions before the time limit you've chosen is over.
Just something to think about, in regard to how your interactivity is set up.

Related

Why is telling jQuery to click my link button slowing my page down?

I don't know if this is the effects of an update panel or what, but I basically have a drop down list that allows a user to select an item as a filter. When the item is selected it should bring back only one item into a grid view. That is this specific filter will at most bring back the record you are looking for. This works fine if the user clicks an "apply" link to apply the filter. Behind the apply link is some server-side code (C# within an ASP.NET Web Forms application).
We had a request by a user with something to the effect of:
"Why do I have to click the apply button if I make a selection in this
one drop down filter...it should simply get that one record I am
searching for. This helps me because I don't have to click the
"Apply" button."
I agreed with him and thought what is the easiest way to do this...I thought: Simple, I will have an on change event handler of the drop down such that when a selection is made I'll trigger a click event. Something to this effect:
$("#MainContent_ddlCompany").on("change", function() {
var companyId = $("#MainContent_ddlCompany").val();
$("#MainContent_hdnCompanyValue").val(companyId);
$("#<%=ddlCompany.ClientID %>").trigger("chosen:updated");
if (companyId.length > 0) {
$(".apply").click();
$(".apply").removeClass("applyButton");
$(".apply").addClass("resetButton");
} else {
//cleared selection of a company
$(".apply").removeClass("resetButton");
$(".apply").addClass("applyButton");
}
});
At first this didn't work, and I couldn't tell why, but then after some serious googling I changed this line:
$(".apply").click();
To this:
$('.apply')[0].click();
That worked great...so I decided to test it some more. As I kept selecting one filter value after another I noticed the page started to slow down. In fact by the 6th or 7th time it was pretty unusable. I don't know why it's happening, but I suspect again it has to do with the fact that this linkbutton with the class name .apply is inside an update panel.
But still I thought to myself, it was inside of an update panel before I changed my jQuery code to simulate the click event. So why does the page slow down and drag with this little piece of code? Is calling the event from jQuery code rendering something else in the HTML that could be causing this?
If I change my code back and force the user to click the apply button then we are back to a good normal speed. Why is it if I tell jQuery to simulate clicking the button my page slow down? It's doing the same thing, the simulation of the click of this link button is calling its server-side code method whether the user clicks it or I have jQuery click it.
For now I'm at a loss as to why this is happening because this button is in an update panel in either case, yet when I have jQuery click it via $('.apply')[0].click(); the page slows down after several attempts. Yet when I have the user simply click this button (without the jQuery click event) then it works fine?
What am I missing here?
Ugh, well, I found my issue. Because I was using updatepanels I had to wrap my jQuery code to include an add_endRequest. That is, you have something to the effect of:
$(document).ready(function() {
//Some initial event/triggers
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function () {
//Copy of some initial event/triggers
});
});
Why do I use the endRequest you ask? Well, because updatepanels basically throw away all your events after an asynchronous postback because the HTML at that point (after an update) is rendered again and at that point all events associated with any control inside an update panel are wiped away. At this point of course document.ready() does not run, so I have to resubscribe to these events inside of endRequest. Enter my issue...
I had a huge brain fart where I basically took everything, literally everything inside document ready and copied it into endRequest. In fact, if I remember correctly, I read articles which stated
Whatever you have in document ready simply copy paste into endRequest
That's fine, but you have to be careful here. I was throwing in events that were not wrapped around inside of an updatepanel into endRequest. The result is disastrous...at least for me.
These events would be attached then multiple times..or based on the number of asynchronous postbacks made. In my case, as I was testing I mentioned after the 6th or 7th time performance starts degrading. Well, by that time my controls were being attached that many times to events. For instance, my .apply button along with my dropdownlist were both outside of my updatepanel. But my jQuery code was attaching the change event of my dropdownlist in both document ready and endRequest.
The result is initially it's pretty fast, because it's only in document ready. But as I make asynchronous postbacks these events are being attached every time. For n tests I would have n attached events...in my case the test of 7 yields 7 on change event handlers!
Case in point, do not place any event handlers such as jQuery's on() event for any controls that are NOT inside an update panel. Otherwise you will run into what I ran into which was poor performance as events are happening.

Unbind inline javascript events from HTML elements in memory

How do I completely unbind inline javascript events from their HTML elements?
I've tried:
undelegating the event from the body element
unbinding the event from the element
and even removing the event attribute from the HTML element
To my surprise at least, only removing the onchange attribute (.removeAttr('onchange')) was able to prevent the event from firing again.
<input type="text" onchange="validateString(this)"></input>
I know this is possible with delegates and that's probably the best way to go, but just play along here. This example is purely hypothetical just for the sake of proposing the question.
So the hypothetical situation is this:
I'm writing a javascript validation library that has javascript events tied to input fields via inline HTML attributes like so:
<input type="text" onchange="validateString(this)"></input>
But, I'd like to make the library a little better by unbinding my events, so that people working with this library in a single-page application don't have to manage my event handlers and so that they don't have to clutter their code at all by wiring up input events to functions in my hypothetical validation library... whatever. None of that's true, but it seems like a decent usecase.
Here's the "sample" code of Hypothetical Validation Library.js:
http://jsfiddle.net/CoryDanielson/jwTTf/
To test, just type in the textbox and then click elsewhere to fire the change event. Do this with the web inspector open and recording on the Timeline tab. Highlight the region of the timeline that correlates to when you've fired the change event (fire the change event multiple times) and you'll see the event listeners (in the window below) increase by 100 on each change event. If managed & removed properly, each event listener would be properly removed before rendering a new input, but I have not found a way to properly do that with inline javascript events.
What that code does is this:
onChange, the input element triggers a validation function
That function validates the input and colors the border if successful
Then after 1 second (to demonstrate the memory leak) the input element is replaced with identical HTML 100 times in a row without unbinding the change event (because I don't know how to do that.. that's the problem here). This simulates changing the view within a single-page app. This creates 100 new eventListeners in the DOM, which is visible through the web inspector.
Interesting Note. $('input').removeAttr('onchange'); will actually prevent the onchange event from being fired in the future, but does not garbage collect the eventListener/DOM stuff that is visible in the web inspector.
This screenshot is after change event fires 3 times. Each time, 100 new DOM nodes are rendered with identical HTML and I've attempted to unbind the onchange event from each node before replacing the HTML.
Update: I came back to this question and just did a quick little test using the JSFiddle to make sure that the answer was valid. I ran the 'test' dozens of times and then waited -- sure enough, the GC came through and took care of business.
I don't think you have anything to worry about. Although the memory can no longer be referenced and will eventually be garbage collected, it still shows up in the Web Inspector memory window. The memory will be garbage collected when the GC decides to garbage collect it (e.g., when the browser is low on memory or after some fixed time). The details are up to the GC implementer. You can verify this by just clicking the "Collect Garbage" button at the bottom of the Web Insepctor window. I'm running Chrome 23 and after I enter text in your validation box about 5 or 6 times, the memory usage comes crashing down, apparently due to garbage collection.
This phenomenon is not specific to inline events. I saw a similar pattern just by repeatedly allocating a large array and then overwriting the reference to that large array, leaving lots of orphaned memory for GC. Memory ramps up for a while, then the GC kicks in and does its job.
My first sggestion would have been to use off('change') but it seems you've already tried that. It's possible that the reason it's not working is because the handler wasn't attached with .on('change'). I don't know too much about how jQuery handles listener like this internally, but try attaching with .on('change', function ()... or .bind('change', function ()... instead.

Detect if an HTML select element is expanded (without manually tracking state)

I'd like to detect if a drop down is expanded or not. I don't want to use extra event handlers for click/mouseover etc because the drop-downs are dynamic and for other reasons I can't use something like jQuery live. Basically I'd like something that can given an arbitrary select element (no other attached event handlers, classes, etc), can give a true/false answer on whether it is expanded or not.
For my specific application, I am handling mouse wheel events, but don't want to handle them when a drop down is open (which would override the browser default functionality). However, I still want to handle the mouse wheel events when the mouse has hovered over the select, but has not opened it.
I looked into this before, for similar reasons. I could never find a solution other than trying to track it manually which really doesn't work. There are several ways to open/close a select (drop down) such as Alt+Dn Arrow. An open select will close if the user clicks on something outside the browser. Trying to keep track of the state of the select is an exercise in futility. Unless someone else comes along with something I missed on my hunt, you'll have to code around it as elegantly as you can.
How about when it's got focus, even if it isn't expanded? You specifically ask for expanded because you don't want to override default browser behaviour, but the browser behaviour should be to scroll through the items when the item is focussed, even if it isn't expanded, so I would say you'd be better off detecting focus.
If you're okay with that, then you can certainly easily detect when a field has focus and when it loses it, by using the JQuery focus() and blur() methods, or focusin() and focusout().
http://api.jquery.com/focus/ and http://api.jquery.com/blur/
http://api.jquery.com/focusin/ and http://api.jquery.com/focusout/
Hope that helps.
Maybe you could do something like this:
$('#dropdown').live('click', function(){
//bind mousewheel here
});
$('#dropdown').live('change', function(){
//unbind mousewheel here
})

javascript events and html

Could you suggest the best way to keep focus on a particular input field on page?
What I am trying to say is, no matter where user clicks on the screen, the focus should return back to this one particular input.
I am thinking of a timer function that would check for focus every 500ms and if its not there, then bring it back.
Also the best way to submit this input field without making use of any button
Any other and best solution for this?
Even if I don't like the idea, thinking about usability and user experience. Anyway, to answer your question, your best shot probably is to set the focus initially to that input element and watch for the
blur event. Addionally you might want to check for outside click events.
That might look like this:
$('#test').bind('focusout', function(e) {
if($(this).val() !== 'releaseme') {
setTimeout(function() {
$(e.target).focus();
}, 25);
}
});
That would force the focus to that input (with the ID "test") 25ms after it lost the focus. To check for the additional outside-click check this link.
Example link: http://www.jsfiddle.net/FX79h/
Again, this sounds not usefriendly. You might want to think about whatever you are trying to do.
Would be a major impediment to disabled accessibility consideration. Remember, some people don't use mice to navigate, and you'd render the site useless to them.
As to allowing submit, you could trap the enter keystroke via Javascript...there's several tutorials on how to do it. However, this would also negatively affect the expected user interaction pattern and disrupt accessibility aids. The button text is the only thing telling a blind user's screen reader that they can submit to the site. Without it, your site is a black hole to them.
I'm all for experimentation and pushing the envelope, but there are reasons that you see so many common elements across the web. Think about it like this--would you stop for a triangular blue stopsign? How about a square pink one? Believe it or not, familiar patterns make for a more comfortable experience. How you "push" familiarity is what determines your success as a UI person. I might add that there's a LOT of bad UI out there....

Is there a profitable way to record user actions in textarea?

I need to send bunch of commands to the server on timer - like:
put(0,"hello")
del(4,1)
put(4," is around the corner")
so I need to monitor and record all of the user input and compile/flush it on the timeout (idle), something like macros.
I can record all things happening onKeyUp/onKeyDown/onMouseDown/onMouseUp using textarea cursor position and keys information (and make it cross-browser some time later) but I can't handle things like pasting using mouse right button and selecting 'Paste' or pasting from the menu (I can handle onChange, but I will have no information is it pasted or already recorded as pressed keys and it fires only after focus change). Even pasting from context menu fires some useful info, but the menu from the browser is the only thing, giving nothing for Javascript.
Is there any plugin for jQuery or something like that and do I really have no other ways to implement it without comparing current-document and document-a-second-before?
Upd.: There are events for handling cut/copy/paste: http://www.quirksmode.org/dom/events/cutcopypaste.html , but what about
the undo one?
P.S. I will show a macro-recording code when I'll finish, if someone really needs it. And to finish it properly, I just need the undo handling possibility. Current version is here: http://code.google.com/p/sametimed/source/browse/WebContent/module-editor.js, look for compileCommands method.
There are events for cut/copy/paste you may listen to, depending on browser. So if they are triggered you may use them, otherwise fall back to more tedious work-around.
See: http://www.quirksmode.org/dom/events/cutcopypaste.html

Categories

Resources