colorbox (which uses live) not rebinding after jQuery ajax call

thaddeusmt picture thaddeusmt · Aug 22, 2010 · Viewed 13.4k times · Source

I have a list of elements that I am loading via ajax (using jQuery's .load()). Each of these elements has an (edit) link next to it that lightboxes (using colorbox) a little edit form. When the lightbox is closed I use the onClosed callback to reload the ajax list to show and changes made during the edit.

The colorbox call looks likes this:

$('.colorbox').colorbox({
  'iframe':true,
  'onClosed':function(){
    $("#featureList").load("/template/featureList","id="+$("#model_id").val());
  }
});

My list looks like this:

<div id="featureList">
  <ul id="features">
    <li id="item_000000000008+0">Element 1 (<a class="colorbox" href="/template/featureLightbox?template_id=000000000008&amp;delta=0">Edit</a>)</li>
    <li id="item_000000000008+1">Element 2 (<a class="colorbox" href="/template/featureLightbox?template_id=000000000008&amp;delta=1">Edit</a>)</li>
  </ul>
</div>

I looked at the colorbox source code and saw that is uses jquery live() to do the bind. Here it is:

$('.' + boxElement).live('click', function (e) {
  if ((e.button !== 0 && typeof e.button !== 'undefined') || e.ctrlKey || e.shiftKey || e.altKey) {
    return true;
  } else {
    launch(this);   
    return false;
  }
});

You can see above the way colorbox works is by binding to "boxElement", which is a class it creates called "cboxElement". Before the live() bind colorbox adds this class (cboxElement) to all of the elements matching the selector (.colorbox in my example), then binds to this new class.

So thought that if I placed the colorbox bind outside of the ajaxed content it would bind to the links after I replaced the #featureList div with ajax, since live() is supposed to bind to elements "now or in the future". But it doesn't because it's binding to .cboxElement instead of .colorbox so when the ajax reloads colorbox doesn't re-add the .cboxElement class to the elements.

I tried calling $.fn.colorbox.init() in the ajax content to get colorbox to re-add the .cboxElement class to the elements, but this had no effect. (I do something like this when dealing with shadowbox, but it doesn't seem to work the same for colorbox.)

So then I tried to place all the colorbox code inside the ajax content. When I do this the colorbox binds are stacking/chaining. So the second time I call it I get two colorboxes (and have to hit the "close" button twice to return to the main screen). The third time I get three. This is because when I call colorbox again it adds the .cboxElement class, making the old live() binds active again, and it also adds ANOTHER live() bind to it. I tried to clear out the .live() binds by calling .die() first, but it didn't work for some reason.

I have found a couple of related posts but none of them solved this problem since colorbox is already using live():
Problem with jQuery Colorbox
jQuery AJAX table to a page but now the colorbox overlays no longer work

Any other ideas out there? I am really stumped. I feel like I should switch to a different lightbox, but in general I like colorbox and it was working great everywhere else on the site until this ajax problem came up.

Thanks!!!

EDIT:

So, in this case my issue was that my framework (Yii) was including a duplicate colorbox script on each AJAX call, which was causing the problem. So watch out for that!

For everyone having issues who is NOT facing the duplicate-script issue I was: @Relic points out below you can sort of "sidestep" some issues by doing your own jQuery delegate() bind which does a "direct call" of colorbox, instead of relying on colorbox's default live() binding. I would tweak it like this for my case:

$(document).delegate("#features a", "click", function (event) { // $.colorbox() call  }

Answer

Eric Hodonsky picture Eric Hodonsky · Jan 24, 2012

First of all, you shouldn't use .live() it's deprecated. Instead learn how to use .delegate() You'll find this is a much more powerful listener and will help solve your problem.

On page load initially the DOM is ready, and colorbox is initilized for the selector AJAX calls a new piece of the page with some DOM element that is in the colorbox selector list, but isn't noticed because it loaded into the page after the selector was read by the javascript.

Try the following - as it watches body #main for all, existing and new a[rel='lightbox']:

$("body #main").delegate("a[rel='lightbox']", "click", function (event) {
                    event.preventDefault();
                    $.colorbox({href: $(this).attr("href"),
                            transition: "fade",
                            innerHeight: '515px',
                            innerWidth: '579px',
                            overlayClose: true,
                            iframe: true,
                            opacity: 0.3});});

EDIT for ".on()"

$("body #main").on("click", "a[rel='lightbox']", function (event) {
                        event.preventDefault();
                        $.colorbox({href: $(this).attr("href"),
                                transition: "fade",
                                innerHeight: '515px',
                                innerWidth: '579px',
                                overlayClose: true,
                                iframe: true,
                                opacity: 0.3
                         });
});

Yes, big change, I know, but the point is the 'on' method can also be used as 'bind', so that's kind of cool.