Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Friday, 4 November 2011

jQuery UI dynamic drag & drop behaviour

In Coconut, we have a feature where you can drag and drop files in folders. What we wanted to do, was drag a file to a folder. When a file is dragged close to a folder, it should open up the subfolder tree, so you can also drop the file on a subfolder. But this was not possible, because the subfolders didn't become droppable. The code to show subfolders when dragging over a folder is similar to this:
jQuery(".dropTarget").droppable({
  greedy: true,
  tolerance: 'pointer',
  hoverClass: 'dropHover',
  accept: function () {
    //code left out
  },
  drop: function (event, ui) {
    //code left out
  },
  over: function (event, ui) {
    this.folderTimeout = window.setTimeout(function () {
      var dropObject = jQuery(this);
      dropObject.find("ul.folderSubList").show();
    }, 500);  
  },
  out: function () {
    window.clearTimeout(this.folderTimeout);
  }   
});

The over and out events are used to open up the subfolder tree. They use a timeout to make the transition less snappy, more smooth, which works fine. When inspecting the HTML with Firebug (after the code above was run), we could not find anything wrong. The hidden subfolders did get the class "ui-droppable" which droptargets receive when the jQuery ui droppable is initialized. So even though the folders are hidden (by css style "display:none;") they were initialized correctly as droptargets.

I couldn't figure out why, so it was time to pair program. My colleague noticed that it looked like the position of the droptargets were cached when a drag starts. A very sharp observation, because this actually is the case! So what we wanted to was refresh the cached positions once a subfolder tree was opened. The jQuery UI draggable and droppable API does not have a method to force a refresh of the positions. I've found a feature request ticket for the refresh method but it seems the method will probably not be introduced until jQuery UI 2.0 (we're at 1.8.16 at this moment).

Most developers have been at this point: you have to implement certain behaviour into your application, but the plugin you're using does not support that behaviour. At this point you have some options:
  1. use another plugin which does support it
  2. find alternative behaviour and ask customer if that's acceptable
  3. convince the customer it's not possible and abondon it
  4. dig into the plugin's core and find a workaround or hack to enable the support *
Of course, the last option is your last resort. You don't want to do this, because you cannot upgrade the plugin and assume your workaround or hack still works. But if options 1 - 3 are not an option (as it was in my case), here's a solution that works with jQuery 1.6.3 and jQuery UI 1.8.13:
jQuery(".dropTarget").droppable({
  greedy: true,
  tolerance: 'pointer',
  hoverClass: 'dropHover',
  accept: function () {
    //code left out
  },
  drop: function (event, ui) {
    //code left out
  },
  over: function (event, ui) {
    this.folderTimeout = window.setTimeout(function () {
      var dropObject = jQuery(this);
      dropObject.find("ul.folderSubList").show();
      jQuery.ui.ddmanager.prepareOffsets(jQuery.ui.ddmanager.current, null);
    }, 500);  
  },
  out: function () {
    window.clearTimeout(this.folderTimeout);
  }   
});

Trick is to call prepareOffsets on the current jQuery UI drop & dragmanager. Now you'll be able to drop on the dynamically shown elements.

*if the plugin is open source, you can also (or maybe should) try to fix it and do a pull request to get the fix to become a part of the plugin

Thursday, 8 September 2011

Internet Explorer 8 crashes with jQuery 1.6.2

Can browser tabs crash? Sure they can. If you're using a buggy plugin or add-on, you can probably crash tabs in any browser. But crashing a tab when using only javascript is probably limited to the ones that have a Microsoft brand on them.
Since august, we've been having problems when our users were accessing Coconut on IE8 in a Citrix environment (and in rare cases also outside a Citrix environment). Which is hard to solve: first of all, I did not have a Citrix account. After the IT department fixed that problem, I was able to reproduce the bug, but had no way to analyze it. In Citrix I couldn't access log files, change IE settings or even view the advanced properties tab. My Google results told me I should try disabling all add-ons, but I wasn't allowed to do this either, even though the IT guys made me local admin on Citrix. It seems that Citrix has all kinds of seperate rules which are enforced on all users, and you have to make specific exceptions to the rules. Just telling Citrix: "hey, this is an admin" is not enough.
Fortunately, my colleague discovered that the error message was the same for each user and he found a stackoverflow topic which had a reference to this error message

If you encounter this error: (this the Dutch version of course)
Or this error:
then you should upgrade your jQuery javascript, version 1.6.4 works fine.

Tuesday, 12 July 2011

jQuery's .html() and Internet Explorer 8

Sometimes it's easy to blame IE for unwanted behaviour. This case is a bit different, in the sense that I think IE's behaviour is understandable, or even correct. I was trying to fix a bug where the contents of an overlay screen would not load in IE8. The contents were fetched using an ajax GET request, after which this piece of code would be executed:

show: function(html) {
  var box = $("#dialog_box");  
  box.html(html);
  box.show();
}

In Firefox or Chrome, this works fine. I verified that the show method was actually called with the html from the GET request. The box.show() call was executed correctly, but somehow the box.html(html) was not. But the method didn't fail either.

After much fiddling around, I've found out that:

Internet Explorer will only insert a HTML string using jQuery's .html() method if every tag in the HTML string is opened and closed properly.

The response from the GET request wasn't valid, it contained one closing div tag too many. IE then refuses to insert the HTML into the DOM. Other browsers seem to have no problem inserting the invalid HTML. So IE8 is correct? Remarkable.