Sylvain Cloutier
Sylvain Cloutier

Reputation: 388

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

Upvotes: 0

Views: 128

Answers (4)

Sylvain Cloutier
Sylvain Cloutier

Reputation: 388

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.

Upvotes: 0

Adder
Adder

Reputation: 5868

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

Upvotes: 1

Danial
Danial

Reputation: 1614

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.

Upvotes: 0

MeanMan
MeanMan

Reputation: 1090

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

Upvotes: 0

Related Questions