order of events when a DOM element event is triggered - javascript

I have a Javascript function bound to the click event of an INPUT element (either checkbox or radio). That function examines the 'checked' property of the DOM element and takes action depending upon whether it is set or not.
If the user clicks on the element as represented in the GUI, the state of the checked property reflects the appearance in the GUI (that is, if the user's click activated the element, the checked property is true).
However, if I trigger the click event (using jQuery), then, when the bound-to-the-click function is invoked, examining the state of the checked property does NOT show the new state. However, the GUI does get updated to present proper representation of the actual state.
Is there a defined order to when these activities are performed? It seems odd to me that the event listener would be invoked before the checked property is set.

Interesting. The .click() called through jQuery has a different behaviour than calling .click() direct on the element. See here :
http://jsfiddle.net/g4aVm/6/
I would take this to indicate that jQuery is doing something non-standard/buggy here.
(I'm slight old fashioned and don't trust jQuery with simple DOM stuff. There's not much to gain by using a library to replicate what already exists, but that's a different discussion.)

Related

Does function scope "lock" a value against changes made in asynchronous events and rapid successive function invocations?

On a portion of a web page, user options are provided though sets of radio buttons, check boxes, and input elements, and a change-event listener is placed on the parent containing all of the options.
The change-event handler builds an object of information necessary to undo/redo the option selections, pushes the most recent change to RAM as a function property, writes it to indexedDB, and, in the transaction oncomplete handler of the write request, logs a description of the change to an area of the document for the user to see, based on the data written to RAM as the property of a function.
If the user edits the value in an input element and, while it still has the focus, clicks a radio button, two change events fire, each invoking the same handler function of the parent's event listener.
The data written to RAM and indexedDB is recorded accurately, but if the user performs the click rapidly, the second change event updates the function property again before the first change event's write transaction completes, such that when the first event's log description is built from the most recent selection held in that function property, it uses the second event's data, resulting in two loggings to the DOM of the second event's selections.
To prevent this from occurring, an independent copy of the option-selection object is assigned to a variable declared locally in the change-event handler function, immediately after it has been built. It seems to be working, for I cannot reproduce what was taking place before no matter how quickly I attempt to click a radio button while an input element with an edit retains the focus.
My question is, does making the copy of the undo object guarantee that the oncomplete handler will always refer to that local function variable to build the log, even if a second change event updates the function property from which it was copied, and makes a new copy, before the oncomplete event fires? Is that local function's variable value "locked" in the function scope, such that the second invocation of it cannot alter what the oncomplete event of the first invocation uses to build the log?
Thank you.
After reading the answer from #Bergi, his use of "shared object" makes something clear to me, for my terminology was poor.
Since the shared object is an array of objects, where each object is data required for undo/redo, each change event needs to modify the shared object so the data is there for potential undo/redo. The issue is not that the second event is adding to the array while the first event's database write completes and before it references the last element in the array to build a log item. The issue is that the log item is always referencing the last object in the array. The undo object doesn't need to be cloned (for it's in RAM as a function property); instead, the array index needs stored in a local variable for reference to build the log from either the last element or the next-to-last element, when two change events take place in rapid succession.
Yes. Every function invocation creates its own local variables, and no code outside of its scope can access them. In your scenario, you also have to take care of not modifying shared objects but cloning them.

Kendo - can two Observable objects use the "change" event on the same page?

I have a page that uses a Kendo MVVM approach for two different elements, one providing file search results, the other a document upload facility.
The problem I am encountering is to do with the change event that both elements use - it seems that when one control fires a change event it is then picked up by the other control, which then attempts to process the event and passes it on, at which point it is picked up by the second control's change handler which processes it and passes it on to the first control's change handler. As you might expect, after around 1500 repetitions of this cycle, I see a Uncaught RangeError: Maximum call stack size exceeded message as the JavaScript engine runs out of memory.
At first I thought the problem was that the container of the second model was contained within the first, but even if they are completely separate on the page it seems as though the problem still shows up, so now I'm wondering whether the problem is related to the event being global to the page.
It seems that anything I do in my event handler in terms of trying to stopPropagation or stopImmediatePropagation - or even to set the event to null altogether - makes no difference to this behaviour. Tracing the call stack I can see it looping through Kendo's trigger call then through the event binding on my object and jQuery's dispatch loops that lead it back to Kendo, where it triggers the event handler on the other observable object.
Removing my bindings does not affect the problem, the change event is still bounced back and forth between Kendo and jQuery in the same way, it just doesn't run through my code.
The answer here was not a direct consequence of Kendo itself, so it would have been hard to answer from the question as I set it.
Inside the Observable container that was raising this error, I was using Isotope for layout. The step I had missed was that I had a relationship like this:
Parent [Observable]
-> Container
-> Child
-> Child
-> Child
One of the things that Isotope brings to the party is that for each item in the child collection, it adds a reference to its parent object.
When the child is Observable that creates a structure like this:
Parent [Observable]
-> Container <--┐
-> Child ---|
-> Child ---|
-> Child ---┘
This is an ideal situation for events to be propagated from child to parent, but because the properties in question were being automagically added by the libraries in question it was very hard to troubleshoot.
The solution was to remove the Container layer from the Observable model - it didn't need to trigger anything on change and so I wrapped it in a simple getContainer() closure and used that everywhere I was previously using it as a property. This protected it from the Observable object, breaking the circular reference without harming the functionality.
It may also be relevant that as far as I can tell the initiating event was a DOM change event rather than one of Kendo's own events. The problem may have been avoidable by using a custom Kendo namespace but that would have been a significant change in a complex application and guaranteed to cause a lot of side effects.

Can't modify properties of element using jquery .on()

I'm currently trying to write what I feel like should be a very simple chrome addon using jquery. I have a tool I use for work that our IT department has stopped supporting Chrome with, because they have enough on their plate troubleshooting IE. Their solution however, was simply to remove the old onClick functions and added the property disabled="diabled" to all of our buttons.
My simple work around for this is using jquery to remove the disabled properly and append the onClick functionality. I've gotten this to work in a few instances, but the problem I'm running into is with new instances of buttons created using ajax forms.
Here's the code I'm currently trying to work with:
function restoreFunctionality() {
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
}
RestoreFunctionality();
Now, this works fine for the initial load, however I'd also like this to work for every button that is to be created in the future. To do this, I added:
$("#RestoreDefaultsButton").on("restoreFunctionality", function(event) {
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
});
This, however, does not work for me but also does not provide any sort of console error message telling me why it won't work. I can't seem to find an example of what I want. I see examples in the jquery doc where it can be called by clicking somewhere or something like that, however what I want is for it to just simply "work". Just look for new instances of that button ID and make the changes.
Is on() not the function I want to use in jquery 1.11.1? Am I somehow using this incorrectly? Any guidance to point me in the right direction would help.
Edit for clarification:
I am not trying to edit the same button multiple times in multiple locations. I am trying and willing to create code individually for each button that comes up, given I know the ID of each one.
Here is an example of something I have that is currently working:
The line of code for the button reads:
<input type="button" name="RestoreDefaultsButton" value="Submit"
id="RestoreDefaultsButton" disabled="disabled" class="aspNetDisabled InlineButtonStyle">
The code that I am using and that actually works just fine is now:
$("body").on("click", "#RestoreDefaultsButton", restoreDefaultFunctionality());
and restoreDefaultFunctionality() is simply:
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
Again, the above code works just fine. What I seem to have trouble with is that not all of my buttons are present on load, I may click a link that loads a model on the same page/url with a form that has additional buttons. That button might read:
<input type="button" name="OpenToolkitButton" value="Submit" id="OpenToolkitButton" disabled="disabled" class="aspNetDisabled InlineButtonStyle">
Which is almost exactly the same as the original example, it's just been loaded after the script ran for the first time.
What I am looking for is a solution to make all individually specified buttons that I need, when they occur, to have that disabled removed and a specific onclick function added.
It appears that you have several things wrong and you are using .on() incorrectly.
First, ids in your document must be unique. You cannot have multiple DOM elements with the same id. That is both illegal HTML and will not correctly work with selectors. So, if you're trying to detect future "#RestoreDefaultsButton" objects in addition to the one you already have, you will have to change that because you can't have more than one and still have selector code work correctly. Usually, you want to use a class name instead of an id when you want to find multiple objects of the same type.
Second, your use of .on() is simply not correct. .on() allows you to register a callback function that will be called when a certain DOM event is triggered. So, when you do this:
$("#RestoreDefaultsButton").on("restoreFunctionality", fn);
You are asking for jQuery to call your function when the single "#RestoreDefaultsButton" object triggers the "restoreFunctionality" DOM event. Since "restoreFunctionality" is not a built-in DOM event, the only way that could ever trigger is if you triggered the event yourself.
The usual solution to modifying newly created objects that are inserted into the DOM is to go find the code that creates those objects and insert a function call (to call your own function that can find and "patch up" the newly created DOM objects right AFTER the newly created DOM objects have been created.
The newest browser versions allow you to register a callback to be notified when certain types of objects are added to the DOM so you could get notified automatically. These notifications are call MutationObservers (doc here). Unfortunately, those events are only implemented in the latest browsers (IE11) so you generally can't solely rely on them for a general web page.
Your click handler assignment could probably be solved with delegated event handling. In delegated event handling for dynamically created objects, you find a persistent object (that is not dynamically created) that will be in the parent chain of your dynamically created element and you bind the click event handler to that parent. Since click events "bubble" up the parent chain, the click event will be seen by the parent. Using the delegated form of .on() that works like this:
$("static parent selector").on("click", "dynamic element selector", fn);
You can then handle the event without worrying about the timing of when the dynamic element is created/destroyed, etc...
You can read more about delegated event handling in these references:
Does jQuery.on() work for elements that are added after the event handler is created?
jQuery .live() vs .on() method for adding a click event after loading dynamic html
jQuery .on does not work but .live does
Are you triggering the "restoreFunctionality" event after your ajax forms are built?
$("#RestoreDefaultsButton").trigger("restoreFunctionality");
Forces it to be synchronous if you have more to do after the call and before you finish the function
$("#RestoreDefaultsButton").triggerHandler("restoreFunctionality");

Is it possible to break on next event in IE10?

I'm working on a project that I haven't been with since the beginning so I don't know the code base very well yet. There seems to be an event on a textbox that runs on blur but I can't find it in the code at all. I'm hoping that IE has the debugger feature where it can break when a particular type of event has been triggered (in my case, the next blur to happen). Or is there a way to get a list of events on an input control?
You can pause script execution in Internet Explorer 10's Developer Tools. This will cause the execution to stop immediately just before the next script is ran. You can then step through execution to target the logic you're hunting for.
Alternatively, if you're binding events with jQuery you can tap into $._data and identify all of the bound events for any given element. For instance, suppose we had an input element that was behaving oddly, we could determine all bound events like this:
// We need a reference to the element, not a jQuery collection
var firstname = $("#firstname")[0];
// Next we pass the element as a reference
console.log( $._data( firstname ) );
This returns the full data object for that particular element. On this resulting object will be an events collection showing all events handled, as well as the handlers ran when those events are raised.
Hope this helps.

document.write and delegated event handler persistence

I'm just testing out replacing a whole page with another page using JavaScript and I found this answer with document.write. As to why document.write, I needed to replace the entire HTML, including scripts and styles using the same page.
It does what I want but i can't seem to have consistency with my event handlers. My handlers are all attached to document using:
$(document).delegate(...);
Currently, I have weird results. In a fiddle I made, it attaches a handler. When clicked, the event fires, rewrites the page, runs the function again - but it doesn't attach the handler.
However in my project, I'm doing the same routine (d.w(), then add handlers). It does reattach once and handlers work, but after doing a second routine (still on the same page), it doesn't attach anymore.
So my questions are:
When using d.w(), do existing handlers get erased from document?
Are window as well as document the same after subsequent d.w()s? or are they somehow "renewed"
Do scripts that are already parsed stay in memory and run after subsequent d.w()s? Or do they get erased as well?
(The following applies to google chrome)
Only the document is cleared, the scripts in memory still stay the same. You can easily test it by setting something to a variable and see if it exists after clearing out the document with .open.
The old native handler is therefore lost from the document, but jQuery still thinks that the handler exists in its own event model. You can see it by editing the log to:
console.log('patch', JSON.stringify($.cache ));
jQuery only ever attaches a single native handler per event, so if you have a "click" event registered on document, further handlers attached with jQuery don't attach a new native handler, instead the handler is pushed into the jQuery internal handlers array.
Now, because document.open removed the native handler, but doesn't clear javascript, jQuery still thinks the native handler exists, and further .delegate only goes to the jQuery internal handler array. If you replace your handler with plain old document.onclick you will see it starts working.
You can also keep using jQuery if you add $(document).unbind() (or more robust $.cache = {};, but this is internal and subject to change) before the .delegate, so that jQuery is again synced. Otherwise it won't be, since it has no idea you called document.open.
So:
Yes
They are still the same objects, can be tested by saving a reference and checking that agaist document after a .open
They stay in memory.
http://jsfiddle.net/wphzt/4/
The only reason it stops working from second time onwards is because in your function you have written
document.write('<span>'+(++i)+'</span>');
In which case, next time the document doesn't have the delegate function to increment the span value but has only what you have written in the code snippet I have highlighted above. Thus, as you doubted, yes they get erased as well. Hope this helps.

Categories

Resources