Foliotek Development Blog

Setting a threshold on jQuery ajax callbacks using prefilters

Wednesday, May 18th, 2011

Skip straight to the demo.

When developing interfaces that include ajax functionality there will come a time when you will be showing some sort of loading animation and it will only show for a split second since the ajax call finished so fast. ?This can cause the user to be disoriented since they aren’t exactly sure what popped up. ?It can be beneficial to slow down the interface so the user can see everything that is going on.

In jQuery 1.5, there is now a way to extend the $.ajax method. ?There are three different ways to extend $.ajax: prefilters, converters, and transports. ?I am going to use prefilters which the jQuery documentation describes as “generalized beforeSend callbacks to handle custom options or modify existing ones”. ?I’m not going to go into details about what a prefilter is since the?documentation does a pretty good job.

Instead of setting up timeouts and clearing them out, I am wanting to pass a custom option to the ajax method. ?Here is what I am trying to go for:


$.ajax(url, {
    ...
    successThreshold: 3000,
    ...
})

The successThreshold option is my custom option. ?The time passed into it will be the minimum amount of time it takes before the success callback gets called. ?Now that I have my custom option, I can access it and modify the other options in my prefilter.


$.ajaxPrefilter(function?(options,?originalOptions,?jqXHR)?{
    if?(originalOptions.successThreshold && $.isFunction(originalOptions.success))?{
        var?start,?stop;
        
        options.beforeSend?=?function?()?{
            start?=?new?Date().getTime();
            if?($.isFunction(originalOptions.beforeSend))
                originalOptions.beforeSend();
        };
        
        options.success?=?function?(response)?{
            var?that?=?this,?args?=?arguments;
            stop?=?new?Date().getTime();

            function?applySuccess()?{
                originalOptions.success.apply(that,?args);
            }

            var?difference?=?originalOptions.successThreshold?-?(stop?-?start);
            if?(difference?>?0)
                setTimeout(applySuccess,?difference);
            else
                applySuccess();
        };
    }
});

The first thing I do in the prefilter is check to make sure both the successThreshold and success function are set. ?I then override the beforeSend option in order to get the time before the ajax call starts. ?In order to keep the success callback from firing before the threshold, the time difference needs to be calculated. ?If the call didn’t take longer than the difference then set a timeout for the remaining time. ?Otherwise just call the success callback immediately.

I have seen other solutions to this and all of them seem to set timeouts and clear them in different functions and it would have to be repeated for every call. ?This can be defined in one place and used on any ajax call in the application.

I have added the code to my Github repository. ?This demo shows the code in action.

Changing .Net Framework version on a website in IIS without restarting the W3SVC process

Wednesday, August 4th, 2010

If you are changing the .Net Framework version of a website hosted in IIS? you will most likely receive a message that looks like the following

The problem is that if you restart the W3SVC, all the application pools will be recycled.? In my case we are hosting multiple versions of the site in IIS (Live, Staging, etc.).? I didn’t want to recycle every application pool if I didn’t need to.? Plus, if you are storing state information in the worker process it will be lost when the application pool gets recycled.

The dialog points to a command you can run in order to keep the W3SVC process from restarting.? The command is pretty straight forward except for the “IIS-Virtual-Path” part.? The “IIS-Virtual-Path” is the path IIS uses in order to differentiate between sites.? This path is in the form “W3SVC/Site ID/root”.? To find the Site ID, just click on the “Web Sites” folder in IIS and you should see a table in the right column that looks similar to this:

IIS 6

IIS 7

Once you find the Site ID, open up a command prompt window and navigate to the folder of the framework version you are wanting to change to.? The framework version folders are usually found in c:/Windows/Microsoft.Net/Framework/.? Once you are inside the folder? you can just run the following command for a site with the ID 1957079098:


aspnet_regiis.exe -norestart -s W3SVC/1957079098/root/

After you update the framework version, you will still need to restart the application pool for the website that you are changing though.

Using the Web.Config connection string with LINQ to SQL

Friday, June 11th, 2010

When updating a project to use LINQ to SQL, I found an issue with deploying to multiple environments. ?Each environment (development, staging, live) had its’ own database associated with this. ?Since I had the .dbml in another assembly, it was only reading from the app.config in the assembly it resided in. ?I was storing the database connection string in the web.config of the project so I thought it would be nice to just use that instead of the app.config.

The first thing I needed to do was to keep the .dbml file from reading from the app.config. ?After opening up the .dbml file, I opened the properties window for the file. ?In the properties window, there is a setting for “Connection”. ?In the “Connection” dropdown I selected the “(None)” selection. ?That keeps the .dbml file from accessing the app.config for the database connection string.

??

The "Connection" setting in the .dbml Properties

Now I needed to get my MainDataContext to use the Web.Config connection string. ?For this I created a partial class for my MainDataContext and created a constructor that passed the connection string from the Web.Config.


public partial class MainDataContext
{
    public MainDataContext()
        : base(System.Configuration.ConfigurationManager.ConnectionStrings["Database.connection.string.from.web.config"].ToString(), mappingSource)
    {
        OnCreated();
    }
}

Now when I deploy to different environments the .dbml file is accessing the correct database instead of the same one from the app.config.

Using jQuery and YQL to get an RSS feed from a site

Friday, February 26th, 2010

Finding an RSS feed on a site can sometimes be a hassle.? Browsers have started introducing a feature that will include an RSS icon in the address bar when the browser finds the site has an RSS feed.? I thought it would be nice to allow users to just enter in a web address and let us do the work finding the RSS feed for the site.? I looked into how browsers were accomplishing this and found that browsers look for a <link> tag in the <head> of the document with the type attribute of “application/rss+xml”.

Now that we know what to look for, how do we go about determining if the website provided has the correct <link> tag?? I decided to let YQL do the work for me.? I loaded up the YQL Query Console and began testing some queries.? On the right hand side, there are different Data Tables you can use.? I scrolled down to “data” and selected the “html” option which allows you to query the html of any site.

The example that you start out with looks similar to this:


select * from html where url="http://finance.yahoo.com/q?s=yhoo" and xpath='//div[@id="yfi_headlines"]/div[2]/ul/li/a'

From this example it is easy to see which URL is being queried and the XPath that is being used to find elements on the page.? If you aren’t familiar with? XPath, check out the W3 XPath documentation.? So now we just plug in the URL of the site we want to search and change the XPath to whatwe need to use to find the <link> elements. Now the query looks like this:


select * from html where url="http://lanitdev.wordpress.com/" and xpath='//link[@type="application/rss+xml"]'

In the query console, be sure to select the JSON radio button and then click the “Test” button.? You will see JSON output that looks like the following:


feeds({
    "query":{
    "count":"1",
    "created":"2010-02-26T09:08:43Z",
    "lang":"en-US",
    "updated":"2010-02-26T09:08:43Z",
    "uri":"http://query.yahooapis.com/v1/yql?q=select+*+from+html+where+url%3D%22http%3A%2F%2Flanitdev.wordpress.com%22+and%0A++++++xpath%3D%27%2F%2Flink%5B%40type%3D%22application%2Frss%2Bxml%22%5D%27",
    "results":{
        "link":{
            "href":"http://lanitdev.wordpress.com/feed/",
            "rel":"alternate",
            "title":"The Lanit Development Blog RSS Feed","type":"application/rss+xml"
        }
    }
}
});

We can see from the JSON that we found 1 feed and we get all the information to go along with it.

Now that we have a proper YQL query,let’s use some jQuery to create a form that retrieves these feeds for us.? I am going to create a simple form where a user inputs a url and then outputs a list of RSS feeds or a message stating that an RSS feed couldn’t be found.

The form is straightforward.? I just have a textbox and a button.? I also have an unordered list which I’ll use to display the list of RSS feeds.


<table>
    <tr>
        <td>
            <label><input type="text" id="website"></label>
        </td>
        <td>
            <input id="getrss" type="button" value="Get RSS">
        </td>
    </tr>
</table>

<ul id="rsslist">
</ul>

Next is the jQuery.? We will attach an event to the button that will hit the YQL URL and return JSON that we can parse and get the data we need.? The YQL URL we will be using is found in the YQL Query Console next to where you entered the query.? There is a box called “The REST Query” which has the query that we will use.? The only modification I made to the rest query was that I took out the URL that was in there and replaced with with the jQuery value from the textbox.


$(function(){
    $("#getrss").click(function(){
        $("#rsslist").empty();
        $.ajax({
            type: "GET",
            url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22"  + $("#website").val() + "%22%20and%0A%20%20%20%20%20%20xpath%3D'%2F%2Flink%5B%40type%3D%22application%2Frss%2Bxml%22%5D'&format=json&diagnostics=false",
            dataType: "json",
            success: function(data){
                if (data.query.count == 1){
                    $("#rsslist").append("<li><a href="" + data.query.results.link.href + "">" + data.query.results.link.title + "</a></li>");
                }
                else if (data.query.count > 1){
                    for (var i = 0; i < data.query.results.link.length;="" i++)="" {="" var="" link="data.query.results.link[i];"><li><a href="" + link.href + "">" + link.title + "</a></li>");
                    }
                } else {
                    $("#rsslist").append("<li>No RSS feed found</li>");
                }
             },
             error: function(){
                 console.log("error");
             }
        });
     });
})

When the AJAX request finishes successfully we check to see if any feeds are returned and if there are, we loop through them and display them.

I created a demo page using jsbin.com so you can see this in action. View Demo

I hope this small example gives you an idea how you can use jQuery and YQL to accomplish other interesting challenges.

Getting the width of a hidden element with jQuery using width()

Monday, December 7th, 2009

UPDATE #3: It needs to be noted that the fix introduced in jQuery 1.4.4 is only for the width() and height() methods. ?If you need inner/outer dimensions the method below still needs to be used. ?I have updated the method to return height/width, outer height/width, and inner height/width. ?There is an optional parameter to include margins in the outer dimension calculations. ?Thanks Ryan and Fred for the heads up.

UPDATE #2: jQuery 1.4.4 was released today (11/11/2010) and included in the release was an update to the width() and height() methods.z ?Each method will now return the correct dimension of the element if it is within a hidden element. ?For further information, you can view the bug report.

UPDATE #1: Based on the feedback in the comments regarding the use of the swap method, I am updating this post with a solution from Ryan Wheale.? He created a function to return the dimensions of an element that is hidden or nested within 1 more hidden elements.? Here is the code that he posted below in his comment:


//Optional parameter includeMargin is used when calculating outer dimensions
(function($) {
$.fn.getHiddenDimensions = function(includeMargin) {
    var $item = this,
        props = { position: 'absolute', visibility: 'hidden', display: 'block' },
        dim = { width:0, height:0, innerWidth: 0, innerHeight: 0,outerWidth: 0,outerHeight: 0 },
        $hiddenParents = $item.parents().andSelf().not(':visible'),
        includeMargin = (includeMargin == null)? false : includeMargin;

    var oldProps = [];
    $hiddenParents.each(function() {
        var old = {};

        for ( var name in props ) {
            old[ name ] = this.style[ name ];
            this.style[ name ] = props[ name ];
        }

        oldProps.push(old);
    });

    dim.width = $item.width();
    dim.outerWidth = $item.outerWidth(includeMargin);
    dim.innerWidth = $item.innerWidth();
    dim.height = $item.height();
    dim.innerHeight = $item.innerHeight();
    dim.outerHeight = $item.outerHeight(includeMargin);

    $hiddenParents.each(function(i) {
        var old = oldProps[i];
        for ( var name in props ) {
            this.style[ name ] = old[ name ];
        }
    });

    return dim;
}
}(jQuery));


This basically performs the same operations as the swap method.? This is safer to use in case the swap method is removed from the jQuery core.

I have tested this in multiple cases and each time the correct results were returned.

Thanks Ryan.

————————————————————————————————————————————-

Original post

I recently ran into a problem with jQuery’s width(). ?The problem is with a visible element that is inside a hidden element will return a value of 0 instead of its’ actual calculated width. ?After messing around with it for a little bit I finally came up with a solution. ?The method I used involved adding some CSS properties to the hidden element. ?The CSS properties involved are position, visibility, and display.


//$HiddenItem is the element that is wrapping the element that you want the width of
//$Item is the element you want the width of

$HiddenItem.css({
    position: "absolute",
    visibility: "hidden",
    display: "block"
})
$Item.width();

$HiddenItem.css({
    position: "",
    visibility: "",
    display: ""
})

After setting the above CSS properties on the element, you can then call width() and the correct value will be returned. After you call the width() method you should clear the properties in order to return the element to the way it was.

Setting the properties to an empty string is probably not the best way to do it though. What if there was a position value already set? Using this method would clear out that initial values of the CSS properties.

I found the swap() method to be handy in this situation. I found this method while looking through the jQuery source code.

 // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) {      var old = {};       // Remember the old values, and insert the new ones      for ( var name in options ) {           old[ name ] = elem.style[ name ];           elem.style[ name ] = options[ name ];      }       callback.call( elem );       // Revert the old values      for ( var name in options ){         ?elem.style[ name ] = old[ name ];      } } 

By using the swap method, the old CSS properties will be remembered and reapplied after finding the width of the element. The swap method takes in 3 parameters:

  1. The element that you would like to swap the CSS properties on
  2. CSS key/value pairs that you want to change
  3. A callback function to call after the properties are set

To rewrite the above to use the swap method I would do the following:


var props = { position: "absolute", visibility: "hidden", display: "block" };
var itemWidth = 0;

$.swap($HiddenItem[0], props, function(){
     itemWidth = $Item.width();
});

//Use itemWidth


I coded up a small example on jsbin. Here is the link http://jsbin.com/ofine3/2.

$HiddenItem.width();