I have an existing system built using jQuery, Backbone.js and a REST-ish back-end written in C#. The application is an SPA with forms to fill and navigation. My job is to build a "Navigation Interceptor" to connect on the application so the system detects whether a field was modified in the current view so that when the user navigates, he will be warned that fields were modified and be requested to save or cancel the changes.
The way I designed it is using jQuery. To make it short, I use a selector on input, select, etc.. and bind a change event to it. Then I use a selector on links and buttons, unbind all click events, bind my "interceptor" (if a field has changed, ask before navigating) and then rebind all click events after my interceptor. I use stopImmediatePropagation() to cancel the regular navigation events, resulting in a kind of wrapper around the events.
By doing so, I have 2 problems:
Calling .val() on a field does not trigger the change event which is fine since I populate the fields dynamically. The problem is that the bootstrap date pickers does not seem to be setting the value using .val() resulting in all date fields having the "changed" state when initialized.
Elements dynamically created (e.g.: field in accordion panel created after the page has loaded) don't accept the events resulting in forms not firing the change event of my navigation interceptor.
My question is regarding the 2 above elements:
Is there a way to determine if a specific field is a date picker and bind the change event on that field so that when I populate it, the change event does not fire, but when the users do, it does (I tried binding on the changeDate event but the setDate method seems to be firing the changeDate event also)?
Is there a way to determine if the element was dynamically created (e.g.: $(''))? The problem is that I do not have a specific selector for a single field, so I think I cannot use delegation ($(document).on('change', 'someFieldSelectorICannotKnow', function () {});). All I have is a handle on the jQuery element ($(this) in a .each(fn) iteration).
#2 Solved using event delegation on all fields and skipping the handler if the field is not a form field
Solution of #2:
NavigationInterceptor.prototype.bindChangeEventOnAllEditableFields = function () {
var self = this;
var fieldsSelector = $(this.configuration.selectors.formFields.join(', '));
$(fieldsSelector).each(function () {
var isFormField = !self.searchClassFromArrayInElement(self.configuration.classes.nonFormFieldClasses, $(this));
if (isFormField && self.configuration.debug.highlight.fields.unchanged && $(this).attr('type') === 'radio') {
$(this).parent().css('background-color', self.configuration.debug.highlight.fields.unchanged);
} else if (isFormField && self.configuration.debug.highlight.fields.unchanged) {
$(this).css('background-color', self.configuration.debug.highlight.fields.unchanged);
}
});
$(document).on('change', fieldsSelector, function (event) {
var field = $(event.target);
var isFormField = !self.searchClassFromArrayInElement(self.configuration.classes.nonFormFieldClasses, field);
if (isFormField) {
self.hasFieldChanged = true;
if (self.configuration.debug.highlight.fields.changed) {
if (field.attr('type') === 'radio') {
field.parent().css('background-color', self.configuration.debug.highlight.fields.changed);
} else {
field.css('background-color', self.configuration.debug.highlight.fields.changed);
}
}
}
});
return this;
}
var unchangeable_classes = ['decorative', 'constant'];
$(document).on('change', 'input,select,textarea', function () {
var $this=$(this);
for(var i =0;i<unchangeable_classes.length;++i){
if($this.hasClass(unchangeable_classes[i]))
return;
}
global_changed = true;
});
Why doesn't this work, it should? (Edited in response to comment.)
My thoughts>>
1)A way to stop calling changeDate() after calling setDate(), you could just call event.stopPropogation(), it will prevent the event from bubbling up
2)while creating a dynamic element, you could add any property of your wish. For eg
var iDiv = document.createElement('div');
iDiv.isDynamic = true;
then while iterating through the element, check for isDynamic property
Why not add a benign class tag to the element
$('#foo').addClass('bar');
then you can check for the class to see if it was created
if ($('#foo').hasClass('bar'))
alert('was created');
Note that when you add elements you have to re-attach the events. So if you have a global document event and then add an element, that element won't be included unless you explicitly attach the new element.
I'm posting the answer for my question #1.
What I did is modify bootstrap's source. When calling setDate like so:
$('#myDateInput').datepicker('setDate', new Date());
The code goes through the function setDates which calls update and setValue, the first one resulting in the date being set in the datepicker itself, the second one setting only the value in the input text field. What I did is remove the call to 'change' which triggers the change event on the field and left the custom event 'dateChange'. This results in my code not firing the change event when I call setDate, but calls it when the user sets a date using the picker itself.
Related
I have a webpage with a small survey. I want to pre populate some of the answers based on user inputs to previous question.
In the below code, if value of id QR~QID3 depends upon value of QID1_Total. However after the page loaded and even if the condition is met the textbox is not populated with correct value.
.addOnload(function()
{
if(document.getElementById("QID1_Total").value>15) {
document.getElementById("QR~QID3").value = "Good";
}
else{
document.getElementById("QR~QID3").value = "Average";
}
});
$("#QID1_Total").on("input", function() {
//statements goes here
});
use of on("input" will track every inputting event, include drop and paste.
know more about onInput : https://mathiasbynens.be/notes/oninput
Here is an Fiddle Example to know how trigger works :
https://jsfiddle.net/5sotpa63/
An Assumption
Let Us Say you are using a function, which holds this statement show Good and Average according to users Input.
var targetElem = document.getElementById("QID1_Total");
var showComment = (targetElem,value>15) ? "Good" : "Average";
document.getElementById("QR~QID3").value = showComment;
Above code is the shorter method of your own statement mentioned in your question.
Now on Change of the target QR~QID3 you need to load some content. you utilize the below code as follows.
$("#QR~QID3").on("input", function() {
//your next question loading statements goes here,
//statements to proceed when you show some comment Good or Average
}).trigger("input");
Hope! this could be helpful.
$('#QID1_Total').keydown(function () {
//ur code
});
as the mouse key is pressed in the input field the function is called
You need to add an event listener to the "QID1_Total" element.
If you want to run the check while the user changes the input, i.e. after each keypress use the oninput event.
If you want to run the check after the user has completed the input, use the onchange event. The onchange event will only fire after the input loses focus.
You can bind the event listeners by using the addEventListener() function like this:
document.getElementById("QID1_Total").addEventListener("input", function(){
//Code goes here
});
Here is a JSFiddle showing both methods.
You also have to use the parseInt() function on the textbox values before you can perform mathematical functions with them.
I am attempting to add an event handler to an anchor only when certain form fields are populated, like so:
$('#newName, #newFrom').keyup(function (e) {
if ($('#newName').val() || $('#newFrom').val()) {
$('#add-person').click(function (e) {
//Handle event, includes adding a row to a table.
$('this').off();
});
}
});
It seems like the first event is getting propagated to the second one since I end up with the same number of rows in my table as keys I have typed.
I've tried adding
e.stopPropagation();
But with no success.
$('this').off(); should be $(this).off();
also probably you'd better go using the input event instead of keyup. input event will trigger even if one pastes content into your fields.
nevertheless I'd go the other way around:
// (cache your selectors)
var $newName = $("#newName"),
$newFrom = $("#newFrom");
// create a boolean flag
var haveNewValue = false;
// modify that flag on fields `input`
$newName.add( $newFrom ).on("input", function() {
haveNewValue = ($.trim($newName.val()) + $.trim($newFrom.val())).length > 0;
});
// than inside the click test your flag
$('#add-person').click(function (e) {
if(!haveNewValue) return; // exit function if no entered value.
// do stuff like adding row to table
});
What was wrong:
on every keyup you was assigning a new (therefore multiple) click event/s to the button, but the (corrected to:) $(this).off() was triggered only after an actual button click.
Also a better way to use .on() and off.() (notice the difference in using the .click() method and the .on() method) is:
function doCoffee() {
alert("Bzzzzzzzz...BLURGGUZRGUZRGUZRG");
}
$("#doCoffeeButton").on("click", doCoffee); // Register "event" using .on()
$("#bossAlertButton").click(function() {
$("#doCoffeeButton").off("click"); // Turn off "event" using .off()
});
I have a pretty simple form. When the user types in an input field, I want to update what they've typed somewhere else on the page. This all works fine. I've bound the update to the keyup, change and click events.
The only problem is if you select an input from the browser's autocomplete box, it does not update. Is there any event that triggers when you select from autocomplete (it's apparently neither change nor click). Note that if you select from the autocomplete box and the blur the input field, the update will be triggered. I would like for it to be triggered as soon as the autocomplete .
See: http://jsfiddle.net/pYKKp/ (hopefully you have filled out a lot of forms in the past with an input named "email").
HTML:
<input name="email" />
<div id="whatever"><whatever></div>
CSS:
div {
float: right;
}
Script:
$("input").on('keyup change click', function () {
var v = $(this).val();
if (v) {
$("#whatever").text(v);
}
else {
$("#whatever").text('<whatever>');
}
});
I recommending using monitorEvents. It's a function provide by the javascript console in both web inspector and firebug that prints out all events that are generated by an element. Here's an example of how you'd use it:
monitorEvents($("input")[0]);
In your case, both Firefox and Opera generate an input event when the user selects an item from the autocomplete drop down. In IE7-8 a change event is produced after the user changes focus. The latest Chrome does generate a similar event.
A detailed browser compatibility chart can be found here:
https://developer.mozilla.org/en-US/docs/Web/Events/input
Here is an awesome solution.
$('html').bind('input', function() {
alert('test');
});
I tested with Chrome and Firefox and it will also work for other browsers.
I have tried a lot of events with many elements but only this is triggered when you select from autocomplete.
Hope it will save some one's time.
Add "blur". works in all browsers!
$("input").on('blur keyup change click', function () {
As Xavi explained, there's no a solution 100% cross-browser for that, so I created a trick on my own for that (5 steps to go on):
1. I need a couple of new arrays:
window.timeouts = new Array();
window.memo_values = new Array();
2. on focus on the input text I want to trigger (in your case "email", in my example "name") I set an Interval, for example using jQuery (not needed thought):
jQuery('#name').focus(function ()
{
var id = jQuery(this).attr('id');
window.timeouts[id] = setInterval('onChangeValue.call(document.getElementById("'+ id +'"), doSomething)', 500);
});
3. on blur I remove the interval: (always using jQuery not needed thought), and I verify if the value changed
jQuery('#name').blur(function ()
{
var id = jQuery(this).attr('id');
onChangeValue.call(document.getElementById(id), doSomething);
clearInterval(window.timeouts[id]);
delete window.timeouts[id];
});
4. Now, the main function which check changes is the following
function onChangeValue(callback)
{
if (window.memo_values[this.id] != this.value)
{
window.memo_values[this.id] = this.value;
if (callback instanceof Function)
{
callback.call(this);
}
else
{
eval( callback );
}
}
}
Important note: you can use "this" inside the above function, referring to your triggered input HTML element. An id must be specified in order to that function to work, and you can pass a function, or a function name or a string of command as a callback.
5. Finally you can do something when the input value is changed, even when a value is selected from a autocomplete dropdown list
function doSomething()
{
alert('got you! '+this.value);
}
Important note: again you use "this" inside the above function referring to the your triggered input HTML element.
WORKING FIDDLE!!!
I know it sounds complicated, but it isn't.
I prepared a working fiddle for you, the input to change is named "name" so if you ever entered your name in an online form you might have an autocomplete dropdown list of your browser to test.
Detecting autocomplete on form input with jQuery OR JAVASCRIPT
Using: Event input. To select (input or textarea) value suggestions
FOR EXAMPLE FOR JQUERY:
$(input).on('input', function() {
alert("Number selected ");
});
FOR EXAMPLE FOR JAVASCRIPT:
<input type="text" onInput="affiche(document.getElementById('something').text)" name="Somthing" />
This start ajax query ...
The only sure way is to use an interval.
Luca's answer is too complicated for me, so I created my own short version which hopefully will help someone (maybe even me from the future):
$input.on( 'focus', function(){
var intervalDuration = 1000, // ms
interval = setInterval( function(){
// do your tests here
// ..................
// when element loses focus, we stop checking:
if( ! $input.is( ':focus' ) ) clearInterval( interval );
}, intervalDuration );
} );
Tested on Chrome, Mozilla and even IE.
I've realised via monitorEvents that at least in Chrome the keyup event is fired before the autocomplete input event. On a normal keyboard input the sequence is keydown input keyup, so after the input.
What i did is then:
let myFun = ()=>{ ..do Something };
input.addEventListener('change', myFun );
//fallback in case change is not fired on autocomplete
let _k = null;
input.addEventListener( 'keydown', (e)=>_k=e.type );
input.addEventListener( 'keyup', (e)=>_k=e.type );
input.addEventListener( 'input', (e)=>{ if(_k === 'keyup') myFun();})
Needs to be checked with other browser, but that might be a way without intervals.
I don't think you need an event for this: this happens only once, and there is no good browser-wide support for this, as shown by #xavi 's answer.
Just add a function after loading the body that checks the fields once for any changes in the default value, or if it's just a matter of copying a certain value to another place, just copy it to make sure it is initialized properly.
I have a page with a asp:textBox.
In my code behind (VB.net) in the Page Load, depending on such filters, i'm going to enable/disable this asp:textBox in this way:
this.txtB.Enabled = true;
this.txtB.Enabled = false;
In the moment that the user change the context of this asp:Textbox (only if it's enabled) i want to do some stuff, and so i thought to use jQuery in this way:
$('.txtB:enabled').on("propertychange input paste", function () { ... });
The problem is that, in some cases, the textBox that in the beginning was disabled had to become enabled too. And i do that always in jQuery:
$(".txt_PISocAppNom").prop('disabled', false);
But in this case when then the user change the context of the textBox, the previous method didn't catch the changes.
I assume that the problem derive from the fact that in my code behind i'm setting the ENABLE attribute and in jQuery the DISABLE attribut.
I don't know if my supposition is correct and in that case how to change the code to make it work.
Have you tried using 'live' instead of 'on'?
$('.txtB:enabled').live("propertychange input paste", function () { ... });
If your textbox is disabled at the beginning the selector '.txtB:enabled' won't apply to it so the change event will not get attached to it. Using 'live' will make sure that the event also gets attached for the future elements.
'live' has been deprecated in the new versions of jquery. In jquery 1.7+ you can also use $('.parentB').on("propertychange input paste", ".txtB:enabled", function () { ... }); to get the same result as 'live'. I'm using '.parentB' as a selector to get the parent of your textbox.
The problem was that i inserted the
$(".txtB:enabled").on("propertychange input paste", function () { ... });
in the
$(document).ready(function () { ... });
In that case, it was attacked in the early stages only to those items that were enabled. So, if my textbox was at first disabled, and enabled only later it hadn't the .on(...) property.
Very strange thing is happening with the 'change' event of the dropdown list.
Basically I have a dropdown, on change of which i have to do some cross domain web service call. This call is being made from the javascript itself.
For the first time when i change an item in the 'select' list the change event is triggered only once. Next time twice and it grows like this.
Any clue why is it behaving like this?
If code needed for reference i can share. But its a simple 'select' list and 'change' event handler there.
$("#ArtifactSort > select").change(function() {
var rankField= "";
rankField = $("#ArtifactSort > select option:selected").text();
alert('within select change event artifact: '+ rankField );
//Making the text little lighter and showing the loading icon.
//$("#ArtifactPetalContentUL").css("filter", "alpha(opacity: 30)");
$loadingIconForArtifact = addLoadingIcon("ArtifactPetalContentUL", "Artifact");
var refinedStoresLocal= new Array();
for (var storeIndex in _searchResponseForArtifact.searchResult.searchRequestProcessed.stores) {
refinedStoresLocal.push(_searchResponseForArtifact.searchResult.searchRequestProcessed.stores[storeIndex].name);
}
var refinedFiltersLocal = new Array();
for (var filterIndex in _searchResponseForArtifact.searchResult.searchRequestProcessed.filters) {
refinedFiltersLocal.push(_searchResponseForArtifact.searchResult.searchRequestProcessed.filters[filterIndex]);
}
//rankfield.
var rankLocal=new Array();
rankLocal.push(new RankingField(rankField, 1, 0));
//Request object and WS Call.
var _searchRequestForArtifactLocal = getArtifactSearchRequestObject(_queryStringLocal, _memberId, _communityId, _pageNumber, _pageSize, propertiesForArtifact, refinedStoresLocal, ClassificationClusteringObjectsForArtifact, refinedFiltersLocal, rankLocal);
getSearchResponse("successcallForArtifact", _searchRequestForArtifactLocal);
});
Thanks
Subrat.
You must be binding a new handler from within the change handler.. So, each time it runs, it adds an additional instance of the handler to be executed the next time..
Show us the handler you assign to the change event (and how you do it) for a more detailed answer..
[update]
From your code everything seems fine.. Do check the two functions you call though, ( addLoadingIcon and getSearchResponse ) in case they do any jQuery event binding, that might inadvertently apply to the select object..
Also check your RankingField constructor in case it binds any events ...
I had the same issue described by the OP. I found that Chrome Dev Tools helped me verify that it was a multiple event handler issue for me. Open the Dev Tools, use the element picker to select the 'select' object in question, and check on the Event Listeners tab for change handlers.