This is another question that I've covered before, but I think it makes sense to bring it up again. Matt asks:
I need some help with a problem that I have run into multiple times. I've looked all over for a solution and wasted countless hours trying to figure this out but just can't seem to get it.
My problem is with the new AJAX containers in CF8 (cfdiv, cflayout, etc.). I need to know when the container has finished loading successfully; That way i can bind javascript functions to some of the html elements that were loaded dynamically. Right now my workaround has been to use setInterval to constantly check if an element has been loaded but that is just an ugly hack I need to get away from. I know that using the "Coldfusion.navigate" function allows me to specify a callback handler which is exactly what I need, but there are instances where I really can't use this. For example using the cflayoutarea "tabs." The tabs already load the new content when you click on them.. no reason for overriding all this just to use "Coldfusion.navigate." Another example, using the bind attribute on a cfdiv, so the cfdiv updates when a bound ajax grid changes.
I have looked behind the scenes to try and figure out if there was anything I could tie into and found that there is a callback handler coldfusion uses when it does all the binding/loading... "ColdFusion.Bind.urlBindHandler" i just haven't found a smart way to get at this.
I love it when long questions have simple answers. ;) The solution here is the ajaxOnLoad function. As you can probably guess, it lets you run a JavaScript function when a page or container is done loading.
Archived Comments
As the onLoad function can be run from multiple places (think on page load, and then you have several cfpods and cfwindows in the mix as well), using the CF8 functionality how would you know which one was the one that invoked the onLoad event?
I suppose that you could create a list of the loadable items and the value of their last loaded source value (or other appropriate value?), and then poll each one in turn to see if that value had changed, but that seems a bit cumbersome. I'd bet that there is a way to tie directly into each underlying JavaScript object and set an onload for each of those objects. That seems a bit easier to manage rather than have a global onload function.
It is my understanding is that it waits for _all_ of them to finish loading.
Nope, I'm wrong, it just checks in the main area. So yes, you would need to place one in each area you want to check for load status.
I wonder if this is the solution for running Spry inside those cfpod, cfwindow and so on.
"Another example, using the bind attribute on a cfdiv, so the cfdiv updates when a bound ajax grid changes."
Well - in this case you can simply bind the cfdiv's url to the grid. Something like:
<cfdiv bind="url:foo.cfm?id={mygrid.ID}" />
When mygrid.ID changes the cfdiv will re-evaluate the bind statement and fetch the new url.
I think there just be a lack of understanding on binding in general - sounds like a lot of Matt's issues could be solved with simple binds (instead of callbacks, etc). Just my gut feeling...
I think that Matt was more looking for ways to apply event handlers as part of the page processing after the page has loaded for the first time. For example, the page is loaded and the end user clicks a couple of things that then cause a cfpod to change its URL (perhaps with binding as Todd suggests), but when that content has loaded, it has elements within it that need to have some event handlers applied.
Haven't played around with this much, I suppose that the content that is being loaded may include some code that is auto-evaluated when it is loaded that performs the event binding, otherwise, the parent page will need to apply the event handlers and this is what I believe that Matt is looking to do.
Danilo Celic is right... and todd sharp's example code is exactly what I'm using to bind a cfdiv to a cfgrid.
The problem was trying to perform some event binding or DOM manipulation on elements that were dynamically loaded through the cfdiv. There was no way of knowing when the elements were available without asking if they existed over and over. I could have used some inline js in the "dynamic page" but I try to build with a web standards methodology (separate all presentation, behavior, and content). So all the js code is in external files linked by the main page containing the cfdiv.
Ray's recommendation of the ajaxOnLoad function is great and worked. I just put the ajaxOnLoad in the dynamically loaded page (so that function fires everytime the cfdiv's bind is re-evaluated). This is really like just writing some inline js like I mentioned above.. It would be nice to have a js only solution and keep cf and js separate. (Would allow for some nicer code management.) Either way thanks Ray! You're amazing!
I have four menu items that each cause a new page to be loaded in a cflayoutarea. These pages typically contains cfgrids or other ajax heavy UI components. If I get click happy on the menu items, eventually the page just hangs and nothing will happen. I know...then don't do that. My boss didn't like that answer.
Was looking for a way to disable the menu links while ajax calls are still running. I was thinking I coudl just swap out the styles when a call is made, then return it to a normal link when the call returns. Am I way off base on this solution? Not sure I understand how it could be done.
I've used AjaxOnLoad in the past as long as I did not mind it calling javascript that is on the parent page. However, in my latest instance, it is not practical to put the javascript on the parent page. I have a form inside a CFDiv and I need a function to run when that form is finished loading. How can I make the AjaxOnLoad execute javascript code on the same page?
Not that I can think of. In jQuery, you can easily load an item into a div and do X after it's loaded. Did you try ajaxOnLoad() within the content loaded?
I have a form inside of at least 3 cfdivs all 3 bound to cfselect tags till you end up at the form with 3 'selected' drop-downs above the form. I can get the form, once submission has happened, to disappear but I cannot seem to find a way to reset the first cfselect/cfdiv value. I have tried the ajaxonload at the completion of the form submission but it does not seem to be able to 'find' that cfselect field even if I use it's given ID/Name.... I have tried to jquery reset all select fields and even tried it in several different implementations but to no avail. I either end up with a javascript error running it in the CFDiv or it just does nothing. I really like the way the form works, I just can't get it to 'reset' after submission. I'm betting there is something simple I am missing. Thanks in advance!
You have "a form" inside 3 divs? So you have 3 forms? Or you have 3 cfdivs that are bound to one form somewhere else on the page?
well 3 cfselects inside cfform tags and a cfdiv bound to each of them. on the last cfdiv calls the main form that does all the real work. the other cfselect values get transferred to session values so the 'real' form can work with them. so really I have 4 forms and 3 divs.
something like
cfform>
cfselect>
/>
/>
cfdiv>
cfform>
cfselect>
/>
/>
cfdiv>
cfform>
cfselect>
/>
/>
cfdiv>
cfform>
/>
/>
/>
/>
not sure this will post right though, here goes.
Man I'm still not following you. You have 3 cfselects inside cfforms. So you have 3 forms with just a cfselect in them. If I read you right. You have 3 cfdivs bound to the 3 cfselects.
You're third cfdiv calls a real form. No idea what you mean there.
It sounded like you said you wanted the first cfselect to 'reset' on form submission. Given that you know the name of the cfselect you should be able to set selectedIndex to 0.
well the first 3 cfforms are there because the cfselects will not work without being inside them, and I can't seem to get the whole mess to work under one cfform wrap. so that is why I say the 'real' form because it is the only one that contains the input fields that the 3 selects determine are needed based on their choices.
Your right I do know the name/id of the first select field, I have tried using reset and setting select back to 0, but the code won't fire as a JSscript> (this throws the usual error) and I try to use the ajaxonload method and turned the 'reset' into a function and called it, but nothing happens, no error and nothing 'resets'. I'm sry this is very confusing to try to explain. hope this helped a little more. I supposed I could cut the code up and put it here if this form will take it. let me know. Thanks.
ok well an update, I work at night so.
This changes the cfselects value to what I want it:
document.getElementById('typeX').value = 0;
But I cannot get the cfselect to 'refresh' with that value, I have tried these to no avail:
ColdFusion.Event.callBindHandlers('typeX', null, 'change');
$('#typeX').val('0').trigger('change');
ColdFusion.bindHandlerCache('typeX').call();
Run them in the same function as the value setting, or even in a separate function, nothing works, nothing happens except the value change.
Let me know if I can provide any further info for you.
Just on a side note; the cfselects are not bound to anything they either have straight hard coded values given to them or their values are supplied by queries, and the cfselect I am trying to change is using hard coded values.
Unfortunately you've got me here. To be honest, I no longer blog about the CF UI Ajax stuff because it leads to too many problems. I don't recommend people use it at all. If you were to hire me (just putting that as a theory btw - not a proposal ;) I'd rip out what you did and rebuild using jQuery.
Wow that's depressing. There any way to kick it out of the never ending cfdiv? cflocation does not work. That would be one way out, I just haven't really tried that route yet, or even a page refresh. Spent this last few days learning this, guess I'll focus more on jquery from now on.
A page refresh may be easier - window.location.reload I believe (developer.mozilla.org is your friend). Yes - I strongly urge you to skip the UI stuff. It works for simple stuff, but once you build something complex, or not exactly to plan, it leads to issues. That - of course - is my personal opinion and not that of Adobe.
So this is the work around that I was able to figure out. And it is: location.reload(true); that is the only one I could find that would work.
But I ended up adding a parameter to the URL with this:
var url = window.location.href;
if (url.indexOf('?') > -1){
url += '&thanks=true'
}else{
url += '?thanks=true'
}
window.location.href = url;
Which also refreshes the page but also gives me a way to trigger a 'thank you' div that I hide on the page.
Then I set an onclick event on the first cfselect to fire a function that hides the 'thank you' div and removes the prameter I added to the URL WITHOUT refreshing the page again, with this line:
window.history.replaceState("", "", "");
So not so bad, works great...... so far.
Thanks
Spinal
Cool, glad you got it Spinal and thanks for sharing your solution.