Fragmatyc Fragmatyc - 5 months ago 9
jQuery Question

How to determine if an Element was created dynamically

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:


  1. 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.

  2. 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:


  1. 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)?

  2. 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;
}

Answer
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.)

Comments