Detect JavaScript initiated change on a JavaScript created object - javascript

I have a TEXTAREA that is created through an external JavaScript. I am writing new script to detect when the contents are changed. DOM events like "change" and "blur" do not work, since the change is initiated by the other script. I do not have the ability to read/modify the external script.
Any ideas?

If you want full cross-browser support, you will simply have to set up a polling interval and compare the contents each time to what you saw the last time. DOM mutation events aren't going to cut it.
Have fun.

Related

Executing external JS using innerHTML and appendChild

I've been playing around with the Material Design Lite library that Google just launched a few days ago, but have some questions, specifically on how to initiate (or execute?) external JS when the HTML changes using innerHTML and appendChild.
See the first example here. As you can see, the HTML for the menu is already within the HTML file when it is first loaded so the menu works fine.
But in this example, the HTML of the document is modified using JS. However, the menu does not work anymore because the script is not executing, I think.
How can I resolve this issue? What's a better way to achieve this result? I'm a newbie when it comes to JavaScript.
You will need to attach the proper event listener from the library. With this change (adding componentHandler.upgradeAllRegistered(); after appending the item) it should work:
document.body.appendChild(menu);
componentHandler.upgradeAllRegistered();
When the menu button is inserted dynamically (when the user clicks), it doesn't get assigned the event listeners to show the menu. I'm guessing that the material design library parses the HTML when it (the library) gets loaded (since you're loading it at the bottom of your HTML document). Since it's already loaded by the time the user clicks, it doesn't check the new element that has been inserted and can't assign it the event listeners.
If this is the case, you'll need to find a way to get the library to recognize your new button.

Why focus an input on page load instead of inline?

Almost all web pages that I see designed to set the focus to an input box add the code into a body onload event. This causes the code to execute once the entire html document has loaded. In theory, this seems like good practice.
However, in my experience, what this usually causes is double work on the user, as they have already entered data into two or three fields and are typing into another when their cursor is jumped back without their knowledge. I've seen a staggering number of users type the last 2/3 of their password into the beginning of a username field. As such, I've always placed the JS focus code immediately after the input to insure there is no delay.
My question is: Is there any technical reason not to place this focus code inline? Is there an advantage to calling it at the end of the page, or within an onload event? I'm curious why it has become common practice considering the obvious practical drawbacks.
A couple thoughts:
I would use a framework like jQuery and have this type of code run on $(document).ready(.... window.onload doesn't run until everything on the page is fully loaded, which explains the delay you have experienced. $(document).ready(... runs when jQuery determines the DOM has been loaded. You could probably write the same sort of logic without jQuery, but it varies by browser.
I prefer to keep my Javascript separate from my HTML because it allows for a cleaner separation of concerns. Then your behavior is then kept separate from your document structure which is separate from your presentation in your CSS. This also allows you to more easily re-use logic and maintain that code — possibly across projects.
Google and Yahoo both suggest placing scripts at the bottom of the html page for performance reasons.
The Yahoo article: http://developer.yahoo.com/performance/rules.html#js_bottom
You should definitely place the script in the appropriate place if it means the correct user experience -- in fact I would load that part of the script (Used for tabbing inputs) before the inputs to ensure it always works no matter how slow the connection.
The "document.ready" function allows you to ensure the elements you want to reference are in the dom and fires right when your whole document dom is loaded (This does not mean images are fully loaded).
If you want you could have the inputs start out as disabled and then reenable them on document ready. This would handle the rare case the script is not ready yet when the inputs are displayed.
Well if you call it before whole page has loaded you really don't know if the element already has been loaded when you make your call. And if you make your call in pre-hand you should check if the element really exists even if you know it always should.
Then to make the call inline, which might seem ideal. But on the other hand it's really bad if a page takes that long to load that you can make several inputs during the loading phase.
Also you could check if input has been made etc.
Also it is possible to check if any input on page contains focus if($("input::focus, textarea::focus").length)... and otherwise set focus on desired input.
Use the autofocus HTML attribute to specify which element should initially receive focus. This decouples JavaScript and gracefully degrades in older browsers.

Unbind inline javascript events from HTML elements in memory

How do I completely unbind inline javascript events from their HTML elements?
I've tried:
undelegating the event from the body element
unbinding the event from the element
and even removing the event attribute from the HTML element
To my surprise at least, only removing the onchange attribute (.removeAttr('onchange')) was able to prevent the event from firing again.
<input type="text" onchange="validateString(this)"></input>
I know this is possible with delegates and that's probably the best way to go, but just play along here. This example is purely hypothetical just for the sake of proposing the question.
So the hypothetical situation is this:
I'm writing a javascript validation library that has javascript events tied to input fields via inline HTML attributes like so:
<input type="text" onchange="validateString(this)"></input>
But, I'd like to make the library a little better by unbinding my events, so that people working with this library in a single-page application don't have to manage my event handlers and so that they don't have to clutter their code at all by wiring up input events to functions in my hypothetical validation library... whatever. None of that's true, but it seems like a decent usecase.
Here's the "sample" code of Hypothetical Validation Library.js:
http://jsfiddle.net/CoryDanielson/jwTTf/
To test, just type in the textbox and then click elsewhere to fire the change event. Do this with the web inspector open and recording on the Timeline tab. Highlight the region of the timeline that correlates to when you've fired the change event (fire the change event multiple times) and you'll see the event listeners (in the window below) increase by 100 on each change event. If managed & removed properly, each event listener would be properly removed before rendering a new input, but I have not found a way to properly do that with inline javascript events.
What that code does is this:
onChange, the input element triggers a validation function
That function validates the input and colors the border if successful
Then after 1 second (to demonstrate the memory leak) the input element is replaced with identical HTML 100 times in a row without unbinding the change event (because I don't know how to do that.. that's the problem here). This simulates changing the view within a single-page app. This creates 100 new eventListeners in the DOM, which is visible through the web inspector.
Interesting Note. $('input').removeAttr('onchange'); will actually prevent the onchange event from being fired in the future, but does not garbage collect the eventListener/DOM stuff that is visible in the web inspector.
This screenshot is after change event fires 3 times. Each time, 100 new DOM nodes are rendered with identical HTML and I've attempted to unbind the onchange event from each node before replacing the HTML.
Update: I came back to this question and just did a quick little test using the JSFiddle to make sure that the answer was valid. I ran the 'test' dozens of times and then waited -- sure enough, the GC came through and took care of business.
I don't think you have anything to worry about. Although the memory can no longer be referenced and will eventually be garbage collected, it still shows up in the Web Inspector memory window. The memory will be garbage collected when the GC decides to garbage collect it (e.g., when the browser is low on memory or after some fixed time). The details are up to the GC implementer. You can verify this by just clicking the "Collect Garbage" button at the bottom of the Web Insepctor window. I'm running Chrome 23 and after I enter text in your validation box about 5 or 6 times, the memory usage comes crashing down, apparently due to garbage collection.
This phenomenon is not specific to inline events. I saw a similar pattern just by repeatedly allocating a large array and then overwriting the reference to that large array, leaving lots of orphaned memory for GC. Memory ramps up for a while, then the GC kicks in and does its job.
My first sggestion would have been to use off('change') but it seems you've already tried that. It's possible that the reason it's not working is because the handler wasn't attached with .on('change'). I don't know too much about how jQuery handles listener like this internally, but try attaching with .on('change', function ()... or .bind('change', function ()... instead.

back/next button in dynamically constructed webpage (AJAX)

My question is about using Back and Next buttons (of the browser) on an AJAX (dynamical) webpage.
The solution I self came up with:
setInterval(function(){
if (location.hash != hash)
{
hash = location.hash;
app.url = window.location.href.toString().replace('http://xxxxx.nl/xxxx/#!/','')
app.handleURL();
}
}, 500);
this function reads the url(hash) and compares it with the last stored url(hash), every 0.5 second. If url has changed (back/next is pushed) it runs handleUrl() which runs more functions to dynamically build my page.
the problem is, this sort of works BUT when I click an html [A] element or when I change the url in an other way (javascript), that content will be loaded TWICE because of the setInterval()... functionality.
How can I build my HTML/Javascript in such way that my content will always be loaded once,
once when I push back/next
once when I click on an HTML element/use Javascript functions on
runtime
I searched the sh*t out of google for a solution, plz help!
You don't need a timer to check it. Just use the onhashchange event, and fire your AJAX calls when the event is called. This event isn't supported in IE versions below 8, though, so your method seems fine if you need IE support.
Also, it doesn't make sense that they're being called twice for a elements, since there's no reason for the interval to call your AJAX loader twice just because the hash was changed using an a element. You probably have an event listener attached to the a element which causes it to load the AJAX content, which wouldn't be needed since you're detecting any change in the hash, no matter how it was changed.
I suggest using a library for that. It will be tricky to make your own solution. Take a look at these:
http://www.asual.com/jquery/address/docs/#sample-usage
http://benalman.com/projects/jquery-bbq-plugin/

Javascript Events: Getting notified of changes in an <input> control value

I have the following problem:
I have an HTML textbox (<input type="text">) whose contents are modified by a script I cannot touch (it is my page, but i'm using external components).
I want to be notified in my script every time the value of that textbox changes, so I can react to it.
I've tried this:
txtStartDate.observe('change', function() { alert('change' + txtStartDate.value) });
which (predictably) doesn't work. It only gets executed if I myself change the textbox value with the keyboard and then move the focus elsewhere, but it doesn't get executed if the script changes the value.
Is there another event I can listen to, that i'm not aware of?
I'm using the Prototype library, and in case it's relevant, the external component modifying the textbox value is Basic Date Picker (www.basicdatepicker.com)
As you've implied, change (and other events) only fire when the user takes some action. A script modifying things won't fire any events. Your only solution is to find some hook into the control that you can hook up to your listener.
Here is how I would do it:
basicDatePicker.selectDate = basicDatePicker.selectDate.wrap(function(orig,year,month,day,hide) {
myListener(year,month,day);
return orig(year,month,day,hide);
});
That's based on a cursory look with Firebug (I'm not familiar with the component). If there are other ways of selecting a date, then you'll need to wrap those methods as well.
addEventListener("DOMControlValueChanged" will fire when a control's value changes, even if it's by a script.
addEventListener("input" is a direct-user-initiated filtered version of DOMControlValueChanged.
Unfortunately, DOMControlValueChanged is only supported by Opera currently and input event support is broken in webkit. The input event also has various bugs in Firefox and Opera.
This stuff will probably be cleared up in HTML5 pretty soon, fwiw.
Update:
As of 9/8/2012, DOMControlValueChanged support has been dropped from Opera (because it was removed from HTML5) and 'input' event support is much better in browsers (including less bugs) now.
IE has an onpropertychange event which could be used for this purpose.
For real web browsers (;)), there's a DOMAttrModified mutation event, but in a couple of minutes worth of experimentation in Firefox, I haven't been able to get it to fire on a text input when the value is changed programatically (or by regular keyboard input), yet it will fire if I change the input's name programatically. Curiouser and curiouser...
If you can't get that working reliably, you could always just poll the input's value regularly:
var value = someInput.value;
setInterval(function()
{
if (someInput.value != value)
{
alert("Changed from " + value + " to " + someInput.value);
value = someInput.value;
}
}, 250);
Depending on how the external javascript was written, you could always re-write the relevant parts of the external script in your script and have it overwrite the external definition so that the change event is triggered.
I've had to do that before with scripts that were out of my control.
You just need to find the external function, copy it in its entirety as a new function with the same name, and re-write the script to do what you want it to.
Of course if the script was written correctly using closures, you won't be able to change it too easily...
Aside from getting around the problem like how noah explained, you could also just create a timer that checks the value every few hundred milliseconds.
I had to modify the YUI datable paginator control once in the manner advised by Dan. It's brute force, but it worked in solving my problem. That is, locate the method writing to the field, copy its code and add a statement firing the change event and in your code just handle that change event. You just have to override the original function with that new version of it. Polling, while working fine seems to me a much more resource consuming solution.

Categories

Resources