Scenario:
I have a RadCombobox and I have attached functions to most of the events.
One event of the combobox is OnClientBlur and I am using this to check whether value in Combo is "Unassigned" or not. If it is "Unassigned" I need to cancel the onblur event and keep the focus on to the same combo.
This is the javascript which I has been used to cancel the event.
if (sender.get_text() === "Unassigned") {
eventArgs.get_domEvent().preventDefault();
return false;
}
Problem:
When the user tabs out first time of the ComboBox the event gets cancelled and the focus stays on the same combo box (in this case it is the 3rd Combo).
But when the user hits the tab button again the focus moves to the next control.
When I debugged the code I found that when the user first hits the tab button, following line works
eventArgs.get_domEvent().preventDefault();
I can see the preventDefault function, see following snapshot.
but when the user hits the tab button again I get an error and cannot see preventDefault function, see following snapshot
I am not able to understand what is going wrong here. Anyhelp would be appreciated.
Your problem, revolves around the difference between MouseEvents and KeyEvents. And also the way Telerik implement the OnClientBlur event. As far as it doesn't point to a specific type of browser event, each time it gets triggered
As you see in the first snapshot you got clientX and clientY, which means your OnClientBlur derived from a MouseEvent.
Whereas in the second one you got altKey, altLeft, and also there is no button property, which means that this one is a KeyEvent.
The other point here is as you have these fields in the output:
e.bookmarks
e.behaviorPart
e.behaviorCookie
Means you are using one of the old versions of IE4+ to IE7 or IE8, which they have cancelBubble instead of preventDefault.
Sometimes events are not cancelable, and using event.cancelable you can make sure if the current event is cancelable or not.
At the end to fix you code you can simply do this:
if (sender.get_text() === "Unassigned") {
var domEvent = eventArgs.get_domEvent();
if(domEvent.cancelable){
if(typeof(domEvent.preventDefault)==="function")
domEvent.preventDefault();
else
domEvent.cancelBubble = true;
return false;
}
else{
//you can not cancel the event, do something else to make it manageable
}
}
Related
I have a form with two input elements that are somewhat intertwined. In element#1 (element #2 is right after element#1 in the tabindex order), once the user tries to leave that field, I run an ajax call to check if the value entered is already in the database and if so, use the javascript confirm dialog to ask the user a question. The second element, upon gaining focus, automatically pops up a modal window with choices the user can make. I am using Jquery.
I would like to run the "Does this data exist" ajax call as soon as the user leaves the first element. the Blur event seemed to be what I wanted as this existing data check is needed whether the user made a change or not.
My problem using blur, though, is that its handler runs AFTER the first element loses focus and focus jumps to element#2. So, the blur handler from element #1 pops up the confirm screen at the same time element #2's focus handler pops up the choices modal and I now have 2 popups open at the same time.
I would like to give the user the chance to answer the question in the confirmation alert before the choices for the element#2 pop up.
Is there a Jquery event similar to blur, but that runs just BEFORE focus is actually lost? Or, is there a way to prevent the next element from gaining focus until the blur handler from the first element completes?
Trying to stop propagation or preventDefault() in the Blur handler does nothing because the focus on element#2 has already happened before the blur handler runs.
I've tried setting the tabindex of element#2 to -1 and then programmatically focusing on that element when needed, but tabbing away from this element becomes a problem, and reverse tabbing skips it (jumping straight to element#1) - I still want that element in tabindex ordering, but just don't want it to gain focus until element#1 completes its handler that needs to run when it loses focus.
I have tried setting status variables as well but when I add code to handle the transition between the two elements, I end up with similar issues and it presents additional edge cases complexity. I've also tried messing with mousedown and keydown events and trying to prevent the default processing, but that added significant complexity and room for error as well.
Any ideas would be welcome. Thank you.
This solution is a bit of a hack, but accomplishes your goal. The trick is to place what amounts to a "no-op" element that accepts the focus on blur. Then controlling the tab after the AJAX request.
Upon each "blur" event, we test to ensure we capture the correct <input> element (I'll leave those details to you).
After the AJAX request has completed, then focus on the next <input>.
For this demo, type 2 in the second input, then tab. I added a short delay so you can see that it works.
$("input").on('blur', function(e){
if(this.value == 2) {
console.log("do ajax request");
setTimeout((function(){
$(this).next().next('input').focus();
}).bind(this), 500);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input tabindex="1" />
<input tabindex="2" />
<div tabindex="3"></div>
<input tabindex="3" />
Would something like this do the trick?
Have a variable that indicates if it's okay to show the second popup
let allowSecondPopup = true;
Have a variable that indicates whether showing the second popup was postponed
let secondPopupPostponed = false;
Set the variable when the first input receives focus
$("#input1").on("focus", fuction () {
allowSecondPopup = false;
});
Send ajax on blur
$("#input1").on("blur", function () {
//$("#input1").disabled(true);
//$("#input2").disabled(true);
$.post("https://example.com", { }, fuction (response) {
if (secondPopupPostponed) {
// Only show second popup after the ajax-call has finished
showSecondPopup();
allowSecondPopup = true;
secondPopupPostponed = false;
}
});
});
And when the second input receives focus, check the variable
$("#input2").on("focus", fuction () {
if (allowSecondPopup) {
showSecondPopup();
} else {
// We're still waiting for the ajax-call to complete.
// When the ajax-call completes, the callback will show the second popup.
secondPopupPostponed = true;
}
});
I was debugging some blur/click event handling and discovered that using breakpoints or alert popups to debug event handling can block other events from being handled. I've provided a jsfiddle with 2 input fields. One input field has a blur() event handler that alerts when called. If you click the input field to gain focus, and then click the button, the button will trigger an alert popup UNLESS the input field you chose has the blur() handler. The action of the blur handler seems to stop the click() handler from even happening.
I have discovered that the same thing occurs when using breakpoints. Using breakpoints in place of the alerts has the same effect. Breaking in the middle of the blur() will prevent the click() handler from even being called.
Is something going terribly wrong? What would be a good way to stop this from happening?
http://jsfiddle.net/stconrad/vtka7tt3/
$(".text").blur(function(e){
var x = 0;
});
$(".text2").blur(function(e){
alert("blurred")
});
$("button").click(function(){
alert("clicked");
});
Your issue is due to alert() method. When you are calling this method, only a single alert can be displayed at once. Then, here, two alerts are triggered in the same time (almost). Unfortunately, second one is simply ignored.
If you test with console.log(), you can see there are two outcomes in your debugger.
What you can do is to use a FIFO to handle your triggers. Simply append your call to the structure and pop first item. If there is a single call, it will be identical to your current process. Otherwise, first one will be triggered, then other one.
If you are using actions such as alert() which are blocking operations, use callbacks to trigger next operation: when alert is done, pop next item if there are some.
What I'm trying to do is: when the user presses a cancel button, or navigates away of the page through a link or a menu option, I check if there are unsaved changes. If so, I ask the user if he/she wants to save. I can't do this with a javascript confirm window because I sometimes have more than two options, so I can't "hold" everything until the user makes a selection like confirm would. So I though to "save" the event, cancel it's current execution, whait until user makes up his/her mind, then take the action needed according to their answer, then raise back the original event.
So, as a code example of what I thought:
I have this piece of code:
var executingEvent;
function someFunction() {
...
if(existUnsavedChanges) {
showConfirmMessage();
executingEvent = window.event;
if (executingEvent.stopPropagation) { executingEvent.stopPropagation(); } else { executingEvent.cancelBubble = true; }
...
}
}
Is there a way to later on do something like this?
raise (executingEvent);
Sounds a bit complex, I'd also welcome other options :)
to fire an event use
elem.dispatchEvent(event)
Where elem is either the element you bound to or below it in the DOM (so it bubbles up).
Of course if you already stopped propagation the event wont bubble up, so you may want to create a new event object instead.
var ev = document.createEvent("Event");
ev.initEvent(type, true, true);
ev.origEv = originalEvent;
elem.dispatchEvent(ev);
It sounds to me like you're overthinking it - you could just raise the same type of event as the original event (which you would have cancelled) once the user has taken the action that you prompted for.
You can work out what the original event raised was by inspecting properties on the event object e.g. the type of event, the original target, etc.
Ok, so we all know that onChange is used to execute javascript code on a select statement when the option changes. However, if you change a select statement using the arrow keys, the onChange event is not called. Is there a way around this? Please help! I'm OCD I know.
--EDIT 1--
Just tested this in IE and arrow keys do work. Apparently it's just Chrome. ** Goes to check firefox
-- Edit 2 --
Tested in Firefox and realized just before an answer below talked about the onBlur action being required for the change. So the answer here is:
Internet Explorer recognizes onChange events from the keyboard as well as clicking on them.
Firefox and Chrome both require key events to be followed by blur event in order to call onChange.
Now normally, I don't like Internet Explorer, because it's a piece of garbage... But I think I... unfortunately, have to say they got that one right.
My understanding as to the reasoning for the blur event on chrome and firefox is to save resources, but I disagree with that. I feel it should follow the literal interpretation of the command onChange... Sigh... I suppose I'm probably wrong somehow, though.
I would suggest you to write the required code in Key Up event to capture the Key press and and also check for Key Code. Hope this helps
Scrolling through a select box is not considered a change. The change happens when you blur() the select and the new option value is applied to the select element.
Coming back to this, it appears that since the asking of this question, Chrome now fires onChange after key events. Firefox appears to still wait for onblur. http://jsfiddle.net/2aQBN/
$(document).ready(function() {
$("#test").on("change", function() {
console.log("Changed.");
});
});
W3C Specification appears to suggest using an input event instead.
When the input event applies, any time the user causes the element's
value to change, the user agent must queue a task to fire a simple
event that bubbles named input at the input element.
However, no input event appears to fire in Chrome or Firefox for the select element. (Just input elements.)
Test demonstrating the current value vs the last onchange value.
http://jsfiddle.net/teynon/MpyHK/5/
Firefox will change the value onmouseover. The key change will change the value as well. However, the onchange hasn't fired. If the form submits while the user has the select menu open, the currently highlighted option is submitted.
From W3C:
If the multiple attribute is absent, and the element is not disabled,
then the user agent should allow the user to pick an option element in
its list of options that is itself not disabled. Upon this option
element being picked (either through a click, or through unfocusing
the element after changing its value, or through a menu command, or
through any other mechanism), and before the relevant user interaction
event is queued (e.g. before the click event), the user agent must set
the selectedness of the picked option element to true and then queue a
task to fire a simple event that bubbles named change at the select
element, using the user interaction task source as the task source.
There is a LONG discussion at https://bugzilla.mozilla.org/show_bug.cgi?id=126379 about this with many people asking for the arrow keys to work. (And some defending the onchange approach.)
Some users have suggested that the W3C is flat out wrong in the specification for the select element's change event. Instead suggesting we propose changes to the specification for how we expect the select's onchange functionality to work.
The current functionality is clearly not intuitive to a large number of people based solely on the number of bug reports. (Mozilla has 40 marked as duplicates.)
This is a pretty dirty hack, but you can force the the change event to fire by doing this:
element.addEventListener('keyup', function(evt){
evt.target.blur();
evt.target.focus();
}, false);
So you'd register an event listener for change as well, and that function would get called when the user presses a key on the <select> via the code above.
You may want to scope this only to Firefox, but AFAIK you'd have to use UA sniffing for that so it's up to you if that's acceptable.
Source
I'm thinking about something like this (to not trigger event if value wasn't changed):
select.keydown(function(){
var _this = $(this);
var _val = $(this).val();
setTimeout(function(){
if(_this.val() !== _val){
_this.trigger("change");
}
}, 1);
});
Here's a realization of this request. For brevity only showing the code. See https://github.com/ida/skriptz/blob/master/js/fields/select/selection_changed.js for long explanations in comments.
function addSelectionChangedListener(selectionEle, onChangeDoWithEle) {
var selectedIndex = null
function onChangeEvent(eve) {
// If selection-change was caused of an option's click-event:
if(eve.explicitOriginalTarget.tagName.toLowerCase() == 'option') {
// We want to trigger passed event-handler:
onChangeDoWithEle(eve.target)
}
}
function onKeyEvent(eve) {
// Key-event is keydown, remember current selectedIndex:
if(eve.type == 'keydown') {
selectedIndex = eve.target.selectedIndex
}
// Key-event is keyup, if selection changed, trigger passed handler:
else if(selectedIndex != eve.target.selectedIndex) {
onChangeDoWithEle(eve.target)
}
}
selectionEle.onchange = function(eve) onChangeEvent(eve)
selectionEle.onkeydown = function(eve) onKeyEvent(eve)
selectionEle.onkeyup = function(eve) onKeyEvent(eve)
}
I'm wondering if there's a way to capture the iPhone's virtual keyboard's done button event, using JavaScript?
Basically, I just want to be able to call a JS function when the user clicks done.
I was unable to track the 'done' button being clicked. It didn't register any clicks or keypresses. I had to addEventListeners for change, focusout and blur using jquery (because the project already was using jquery).
You need to do some kind of this:
$('someElem').focusout(function(e) {
alert("Done key Pressed!!!!")
});
It worked for me, hope it will help you as well.
After searching and trying this solution
basically is say:
document.addEventListener('focusout', e => {});
tested on IPhone 6s
This question is kinda old, but I've found a hacky way recently to make this working.
The problem with the 'blur', 'focusout' events is that they fire even if user just tapped outside the input/textarea, and did not press the 'Done' button, in my case, UI should behave differently depending on what exactly have happened.
So to implement it, I've done the next thing:
After showing the keyboard (the input received the focus), add click handler on the window via the addEventListener function. When user clicks on the window, remember the timestamp of the click in the variable (let's call it lastClick = Date.now())
In the blur event handler, set a timeout for 10-20 ms to allow other events happening. Then, after the timeout, check if the blur event happened in a time difference lower for example than 50-100 ms than the lastClick (basically Date.now() - lastClick < 50). If yes, then consider it as a 'Done' button click and do corresponding logic. Otherwise, this is a regular 'blur' event.
The key here is that tapping on keyboard controls (including Done button) does not trigger the click event on the window. And the only other way to make keyboard hide is basically tap on other element of the page and make the textarea lose focus. So by checking when the event happened, we can estimate whether that's a done button click or just blur event.
The answer by oron tech using an event listener is the only one that works cross platform.
document.getElementById("myID").addEventListener("focusout", blurFunction);
function blurFunction() { // Do whatever you want, such as run another function
const myValue = document.getElementById("myID").value;
myOtherfunction(myValue);
}
"Change" event works fine
document.querySelector('your-input').addEventListener('change',e=>
console.log('Done button was clicked')
);
attach a blur event to the text box in question. The done fire will fire this event.
The done key is the same as the enter key. So you can listen to a keypress event. I'm writing this using jQuery and i use it in coffee script so I'm trying to convert it back to js in my head. Sorry if there is an error.
$('someElem').bind("keypress", function(e){
// enter key code is 13
if(e.which === 13){
console.log("user pressed done");
}
})