iNETTUTS

How to Mimic the iGoogle Interface

Nov 18th in JavaScript & AJAX by James Padolsey

In this tutorial I'll be showing you how to create a customizable interface with widgets. The finished product will be a sleek and unobtrusively coded iGoogle-like interface which has a ton of potential applications!

PG

Author: James Padolsey

I'm a freelance web developer based in Hampton, UK. I write about and enjoy front-end web development. Most of what I write focuses on my favorite topic, JavaScript! To read my blog or find out more about me please visit my site!

Finished Project

The plan

First, let's list exactly what we'll be creating here and what features it'll have:

  • This interface will contain several widgets.
  • Each widget can be collapsed, removed and edited.
  • The widgets can be sorted into the three seperate columns by the user (using a drag and drop technique).
  • The user will be able to edit the color and title of each widget.
  • Each widget can contain any amount of regular HTML content, text, images, flash etc.

Please note that we will only be covering the front-end aspect of the project in this tutorial. Obviously you could integrate this UI with a solid server-side system which could take care of saving preferences and customised widgets.

Since it's all about the user and because the idea was influenced by iGoogle we're going to be calling this project 'iNettuts'.

The layout of iNettuts

The layout is a simple three column one; each column contains widgets:

layout

Each widget has a "handle" which the user can use to move the widget around.

jQuery UI

As well as the jQuery core library we're also going to make use of the jQuery's UI library and specifically the "sortable" and "draggable" modules. This will make it quite simple to add the drag-and-drop functionality that we want. You should get a personalized download of the UI library which has what we need in it. (Tick the 'sortable' box)

Step 1: XHTML markup

Each column will be an unordered list (UL) and each widget within the columns will be a list item (LI):

First column:

<ul id="column1" class="column">

    <li class="widget red">  
        <div class="widget-head">
            <h3>Widget title</h3>
        </div>
        <div class="widget-content">

            <p>The content...</p>
        </div>
    </li>
    <li class="widget blue">  
        <div class="widget-head">

            <h3>Widget title</h3>
        </div>
        <div class="widget-content">
            <p>The content...</p>

        </div>
    </li>
</ul>

The above code represents the first column, on the left and two widgets each within a list item. As shown in the plan, there will be three columns - three unordered lists.

Step 2: CSS

We'll be using two CSS StyleSheets, one of them will contain all the main styles and the second StyleSheet will only contain styles required by the JavaScript enhancements. The reason we seperate them like this is so that people without JavaScript enabled do not waste their bandwidth downloading styles which they're not going to use.

Here is inettuts.css:

/* Reset */
body,img,p,h1,h2,h3,h4,h5,h6,ul,ol {margin:0; padding:0; list-style:none; border:none;}
/* End Reset */
	
body {font-size:0.8em; font-family:Arial,Verdana,Sans-Serif; background: #000;}
a {color:white;}
	
/* Colours */
.color-yellow {background:#f2bc00;}
.color-red    {background:#dd0000;}
.color-blue   {background:#148ea4;}
.color-white  {background:#dfdfdf;}
.color-orange {background:#f66e00;}
.color-green  {background:#8dc100;}
.color-yellow h3,.color-white h3,.color-green h3
	{color:#000;}
.color-red h3,.color-blue h3,.color-orange h3
	{color:#FFF;}
/* End Colours */
	
/* Head section */
#head {
    background: #000 url(img/head-bg.png) repeat-x;
    height: 100px;
}
#head h1 {
    line-height: 100px;
    color: #FFF;
    text-align: center;
    background: url(img/inettuts.png) no-repeat center;
    text-indent: -9999em
}
/* End Head Section */
	
/* Columns section */
#columns .column {
    float: left;
    width: 33.3%;
		/* Min-height: */
		min-height: 400px;
		height: auto !important; 
		height: 400px;
}
	
/* Column dividers (background-images) : */
	#columns #column1 { background: url(img/column-bg-left.png) no-repeat right top; }
	#columns #column3 { background: url(img/column-bg-right.png) no-repeat left top; }
		
#columns #column1 .widget { margin: 30px 35px 30px 25px; }
#columns #column3 .widget { margin: 30px 25px 30px 35px; }
#columns .widget {
    margin: 30px 20px 0 20px;
    padding: 2px;
    -moz-border-radius: 4px;
    -webkit-border-radius: 4px;
}
#columns .widget .widget-head {
    color: #000;
    overflow: hidden;
    width: 100%;
    height: 30px;
    line-height: 30px;
}
#columns .widget .widget-head h3 {
    padding: 0 5px;
    float: left;
}
#columns .widget .widget-content {
    background: #333 url(img/widget-content-bg.png) repeat-x;
    padding: 5px;
    color: #DDD;
    -moz-border-radius-bottomleft: 2px;
    -moz-border-radius-bottomright: 2px;
    -webkit-border-bottom-left-radius: 2px;
    -webkit-border-bottom-right-radius: 2px;
    line-height: 1.2em;
    overflow: hidden;
}
/* End Columns section */

There's nothing too complicated in the above StyleSheet. Normally it would be better to use images instead of the CSS3 border-radius property to create rounded corners (for cross-browser benefits) but they're not really an integral part of the layout - adding a border-radius is quick and painless.

Just a note about the colour classes: Ideally, elements should be named according to their semantic meaning or content, not their appearance. The problem is that the widgets can mean/contain many different things so having classes like this really is the best alternative, unless you're willing to add the colour styles inline. Each colour class is prefixed with 'color-'; it'll become clear why I've done this later in the tutorial.

In the above CSS we're also using a min-height hack for each column so that the background images (the dividers) remain intact and so that an empty column can still have widgets dragged back into it:

#columns .column {
    float: left;
    width: 33.3%;
	
	/* Min-height: */
		min-height: 400px;
		height: auto !important; 
		height: 400px;
}

We'll focus on the second stylesheet later when we've added the JavaScript.

Here's a preview of what we've got so far, just CSS/HTML (and some images):

layout

Step 3: JavaScript

Introduction

As I've said, we'll be using jQuery. It's the library of choice not only because of the UI modules it offers but also because it will help in speeding up the development process while keeping everything cross-browser operable.

The final product will have endless possibilities, some of which have already been explored by the likes of NetVibes and iGoogle. So, we want to make sure our code is easily maintainable, allows for expandability and is reusable; we want it to be future-proof!

We'll begin with a global object called "iNettuts" - this will act as the sole occupied namespace of the project (plus dependencies such as jQuery). Under it we will code up the main functionality of the site which utilises jQuery and its UI library.

inettuts.js:

var iNettuts = {
	settings : {
	   // Some simple settings will go here.
	},
	init : function(){
		// The method which starts it all...
	}
};

The init method will be called when the document is ready for manipulation (i.e. when the DOM is loaded and ready). While there are various methods available it has been proven that the quickest way to initialise your code upon this event is to call it from the bottom of your document. It also makes sense to link to all the scripts at the bottom so as not to slow down the loading of the rest of the page:

<body>
    
    <!-- page content -->

    
	
    <!-- Bottom of document -->
    <script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js"></script>
    <script type="text/javascript" src="inettuts.js"></script>

    <script type="text/javascript" src="jquery-ui-personalized-1.6rc2.min.js"></script>
    
</body>

Settings

As I've said, there will be a settings object which will contain all of the global settings required to make this functional. We'll also have individual widget settings objects, this means it will be possible to create per-widget settings.

settings object (under iNettuts):

settings : {
	/* Specify selectors */
	columns : '.column',
	widgetSelector: '.widget',
	handleSelector: '.widget-head',
	contentSelector: '.widget-content',
	/* Settings for the Widgets: */
	widgetDefault : {
		movable: true,
		removable: true,
		collapsible: true,
		editable: true,
		colorClasses : ['yellow','red','blue','white','orange','green']
	},
	/* Individual Widget settings: */
	widgetIndividual : {
		intro : {
			movable: false,
			removable: false,
			collapsible: false
		},
		gallery : {
			colorClasses : ['yellow','red','white']
		}
	}
}

Yes, there are quite a lot of settings, but if we want maximum code reusability this is a necessity. Most of the above is self explanatory. As you can see we've setup a widgetDefault object which contains the default settings for each widget; if you want to override these settings then the script will require you to give the widget an id (in the HTML) and then create a new ruleset. We've got two rule-sets (objects) which override their defaults, 'intro' and 'gallery'. So, those rules specified in the "gallery" object will only apply to this widget:

<li class="widget blue" id="gallery">  
    <div class="widget-head">
        <h3>Instructions</h3>
    </div>

    <div class="widget-content">
        <ul>
            <li>To move a widget...</li>
        </ul>
    </div>

</li>

Retrieving the settings

getWidgetSettings object (under iNettuts):

getWidgetSettings : function(id) {
	var settings = this.settings;
	return (id&&settings.widgetIndividual[id]) ? 
		$.extend({},settings.widgetDefault,settings.widgetIndividual[id])
		: settings.widgetDefault;
}

This method will return an object with the settings of any particular widget. If a widget has no id (HTML attribute) then it will just return the default settings, otherwise it will look to see if that widget has settings of its own, if it does then it will return the default settings and that widget's settings merged into a single object (the widget's individual settings have precedence).

Attaching a CSS file using JavaScript

I mentioned earlier that we have an additional stylesheet which the JavaScript enhancements will require.

Here's the StyleSheet (inettuts.js.css):

/* JS-Enabled CSS */
	
.widget-head a.remove  {
    float: right;
    display: inline;
    background: url(img/buttons.gif) no-repeat -24px 0;
    width: 14px;
    height: 14px;
    margin: 8px 4px 8px 0;
    text-indent: -9999em;
    outline: none;
}
	
.widget-head a.edit  {
    float: right;
    display: inline;
    background: url(img/buttons.gif) no-repeat;
    width: 24px;
    height: 14px;
    text-indent: -9999em;
    margin: 8px 4px 8px 4px;
    outline: none;
}
	
.widget-head a.collapse  {
    float: left;
    display: inline;
    background: url(img/buttons.gif) no-repeat -52px 0;
    width: 14px;
    height: 14px;
    text-indent: -9999em;
    margin: 8px 0 8px 4px;
    outline: none;
}
	
.widget-placeholder { border: 2px dashed #999;}
#column1 .widget-placeholder { margin: 30px 35px 0 25px; }
#column2 .widget-placeholder { margin: 30px 20px 0 20px; }
#column3 .widget-placeholder { margin: 30px 25px 0 35px; }
	
.edit-box {
    overflow: hidden;
    background: #333 url(img/widget-content-bg.png) repeat-x;
    margin-bottom: 2px;
    padding: 10px 0;
}
	
.edit-box li.item {
    padding: 10px 0;
    overflow: hidden;
    float: left;
    width: 100%;
    clear: both;
}
	
.edit-box label {
    float: left;
    width: 30%;
    color: #FFF;
    padding: 0 0 0 10px;
}
	
.edit-box ul.colors li {
    width: 20px;
    height: 20px;
    border: 1px solid #EEE;
    float: left;
    display: inline;
    margin: 0 5px 0 0;
    cursor: pointer;
}

The elements targetted in the above StyleSheet have yet to be coded, but eventually we will write the JavaScript which dynamically adds these elements to the page, thus making use of the StyleSheet.

The method which attaches this StyleSheet is called 'attachStylesheet':

attachStylesheet : function (href) {
    return $('<link href="' + href + '" rel="stylesheet" type="text/css" />').appendTo('head');
}

The above method appends a link to the head of the document. When a new link element is added to the document through the DOM the browser will load it and apply its CSS rules as it would for any regular hard-coded linked StyleSheet. When doing this, remember that the rules of CSS inheritance and specificity still apply.

Making the widgets work

Widgets

The next part of the tutorial is probably the hardest, so take it slowly.

We want to add another method to our global iNettuts object, we'll call it makeSortable:

makeSortable : function () {
    // This function will make the widgets 'sortable'!
}

By the way, 'method' is just a fancy name given to 'functions' which have been assigned to object properties. In this case our object is called 'iNettuts' so 'makeSortable' is a method of 'iNettuts'...

This new method will take the settings we specified in the 'settings' object and make the required element sortable.

First, we want to make sure everything we need is easily accessible within this new method:

makeSortable : function () {
    var iNettuts = this, // *1
        $ = this.jQuery, // *2
        settings = this.settings; // *3
}

*1: There will only be one instance of our global object, but just encase multiple instances need to be made or if we want to rename the global object it's a good idea to set a new variable (in this case 'iNettuts') to the 'this' keyword which references the object that this method is within. Be careful, the 'this' keyword is a bit of a beast and doesn't always reference what you think it does!

*2: At the very top of the iNettuts object we've placed a new property: 'jQuery : $'. In the pursuit of maximum code reusability we don't want our script to conflict with any other libraries that also make use of the dollar-sign symbol (e.g. The Prototype library). So, for example, if you renamed jQuery to JQLIB then you could change the 'jQuery' property to JQLIB and the script would continue to function properly. The 2nd line in the above code isn't necessary at all, - if we didn't want it we could just use this.jQuery().ajQueryFunction() instead of $() within this method.

*3: Again, this isn't really necessary, we're just creating a bit of a shortcut, so instead of having to type out 'this.settings' within this method we only need to type out 'settings'.

The next step is to define a set of sortable items (i.e. the widgets that will be movable). Remember, back in the settings we made it possible to set a property called 'movable' to true or false. If 'movable' is set to false, either by default or on individual widgets we have to cater for that:

/*
 * (using the dollar prefix on $sortableItems is a convention when a variable references a jQuery object)
 */
  
$sortableItems = (function () {
    
    // Define an empty string which can add to within the loop:
    var notSortable = '';
    
    // Loop through each widget within the columns:
    $(settings.widgetSelector,$(settings.columns)).each(function (i) {
        
        // If the 'movable' property is set to false:
        if (!iNettuts.getWidgetSettings(this.id).movable) {
            
            // If this widget has NO ID: 
            if(!this.id) {
                
                // Give it an automatically generated ID:
                this.id = 'widget-no-id-' + i;
                
            }
        
            // Add the ID to the 'notSortable' string:
            notSortable += '#' + this.id + ',';
        }
        
    });
    
    /*
    * This function will return a jQuery object containing
    * those widgets which are movable.
    */
    return $('> li:not(' + notSortable + ')', settings.columns);
})();

Now we've got a set of DOM elements referenced in the jQuery object which is returned from the above functions. We can make immediate use of this:

$sortableItems.find(settings.handleSelector).css({
	cursor: 'move'
}).mousedown(function (e) {
	$(this).parent().css({
		width: $(this).parent().width() + 'px'
	});
}).mouseup(function () {
	if(!$(this).parent().hasClass('dragging')) {
		$(this).parent().css({width:''});
	}
});

So, we're looking for what has been defined as the 'handle' within the movable widgets (within sortableItems) and then we're applying a new CSS cursor property of 'move' to each one; this is to make it obvious that each widget is movable.

The mousedown and mouseup functions are needed to work around some issues with dragging and dropping... Since we want this page and all the elements inside it to expand when the browser is resized we haven't set any explicit widths on the widgets (list items). When one of these list items is being sorted it becomes absolutely positioned (while being dragged) which means that it will stretch to its content's composite width. Here's an example:

When dragged the widget stretches to length of page

This is what should be happening:

When dragged the widget has the correct width!

To make this happen we have explicitly set the widget's width to what it was before dragging had begun. The UI 'sortable' module does have a property in which you can put a function that will run when a widget starts being sorted (i.e. when it starts being dragged), unfortunately this is not good enough for us because it runs too late; we need to set the width before the 'sortable' module takes hold - the best way to do this is by running a function on mousedown of the handle (the 'handle', in this case, is the bar at the top of each widget).

// mousedown function:
// Traverse to parent (the widget):
$(this).parent().css({
    // Explicitely set width as computed width:
    width: $(this).parent().width() + 'px'
});

If we leave it like this then when you drop the widget in a certain place and res

the browser the widget will not change in size. In order to prevent this we need to write a function to be tied to the mouseup event of the handle:

// mouseup function:
// Check if widget is currently in the process of dragging:
if(!$(this).parent().hasClass('dragging')) {
    // If it's not then reset width to '':
    $(this).parent().css({width:''});
} else {
    // If it IS currently being dragged then we want to 
    // temporarily disable dragging, while widget is
    // reverting to original position.
    $(settings.columns).sortable('disable');
}

The 'dragging' class is added on that 'start' property of the sortable module which we talked about earlier. (we'll write that code later)

This is what our makeSortable method looks like so far:

makeSortable : function () {
    var iNettuts = this,
        $ = this.jQuery,
        settings = this.settings,
        
        $sortableItems = (function () {
            var notSortable = '';
            $(settings.widgetSelector,$(settings.columns)).each(function (i) {
                if (!iNettuts.getWidgetSettings(this.id).movable) {
                    if(!this.id) {
                        this.id = 'widget-no-id-' + i;
                    }
                    notSortable += '#' + this.id + ',';
                }
            });
            return $('> li:not(' + notSortable + ')', settings.columns);
        })();
    
    $sortableItems.find(settings.handleSelector).css({
        cursor: 'move'
    }).mousedown(function (e) {
        $sortableItems.css({width:''});
        $(this).parent().css({
            width: $(this).parent().width() + 'px'
        });
    }).mouseup(function () {
        if(!$(this).parent().hasClass('dragging')) {
            $(this).parent().css({width:''});
        } else {
            $(settings.columns).sortable('disable');
        }
    });
}

Next, still within 'makeSortable' we need to initialise the 'sortable' module:

makeSortable : function () {
    // ...........................
    // BEGINNING OF METHOD (above)
    // ...........................
    
    // Select the columns and initiate 'sortable':
    $(settings.columns).sortable({
    
        // Specify those items which will be sortable:
        items: $sortableItems,
        
        // Connect each column with every other column:
        connectWith: $(settings.columns),
        
        // Set the handle to the top bar:
        handle: settings.handleSelector,
        
        // Define class of placeholder (styled in inettuts.js.css)
        placeholder: 'widget-placeholder',
        
        // Make sure placeholder size is retained:
        forcePlaceholderSize: true,
        
        // Animated revent lasts how long?
        revert: 300,
        
        // Delay before action:
        delay: 100,
        
        // Opacity of 'helper' (the thing that's dragged):
        opacity: 0.8,
        
        // Set constraint of dragging to the document's edge:
        containment: 'document',
        
        // Function to be called when dragging starts:
        start: function (e,ui) {
            $(ui.helper).addClass('dragging');
        },
        
        // Function to be called when dragging stops:
        stop: function (e,ui) {
        
            // Reset width of units and remove dragging class:
            $(ui.item).css({width:''}).removeClass('dragging');
            
            // Re-enable sorting (we disabled it on mouseup of the handle):
            $(settings.columns).sortable('enable');
            
        }
        
    });
    
}

The above options setup the behaviour we want for our sortable widgets. There are plenty more available options for this module but those above will be sufficient for now.

Editing, removing and collapsing widgets

The next step is to make it possible for the user to collapse widgets, close (remove) widgets and edit certain elements within each widget.

We're going to put this all within one method, we'll call it 'addWidgetControls':


addWidgetControls : function () {
    // This function will add controls to each widget!
}

As with 'makeSortable' we want to set the following variables at the start:

addWidgetControls : function () {
    var iNettuts = this,
        $ = this.jQuery,
        settings = this.settings;
}

We need to loop through every widget on the page and add functionality dependent on the default settings or the settings made for any particular widget.

// Loop through each widget:
$(settings.widgetSelector, $(settings.columns)).each(function () {

	/* Merge individual settings with default widget settings */
	var thisWidgetSettings = iNettuts.getWidgetSettings(this.id);
	
	// (if "removable" option is TRUE):
	if (thisWidgetSettings.removable) {
	
		// Add CLOSE (REMOVE) button & functionality
		
	}
	
	// (if "removable" option is TRUE):
	if (thisWidgetSettings.editable) {
	
		// Add EDIT button and functionality
		
	}
	
	// (if "removable" option is TRUE):
	if (thisWidgetSettings.collapsible) {
	
		// Add COLLAPSE button and functionality
		
	}
		
});

As you can see from the above code, we're checking the settings before adding any one of the three buttons and each button’s corresponding functionality.

Before we write out exactly what will happen within each of three conditions let's list exactly what each of these buttons will do:

  • CLOSE (remove): This button will remove the widget from the DOM. Instead of just removing it immediately we'll apply an effect which will fade out he widget and then slide up its occupied space.
  • EDIT: This button, when clicked, will bring up an 'edit box' section within the widget. Within this 'edit' section the user can change the title of the widget and its colour. To close the 'edit' section the user must click on the same 'edit' button again - so basically this button toggles the 'edit' section.
  • COLLAPSE: This button switches between an up-arrow and a down-arrow dependent whether the widget is collapsed or not. Collapsing a widget will simply hide its content, so the only viewable of the widget will be the handle (the bar at the top of each widget).

We know what we want now, so we can start writing it: (The snippets below are riddles with comments so make sure you read through the code!)

CLOSE (remove):

// (if "removable" option is TRUE):
if (thisWidgetSettings.removable) {
    
    // Create new anchor element with class of 'remove':
    $('<a href="#" class="remove">CLOSE</a>').mousedown(function (e) {
    
        // Stop event bubbling:
        e.stopPropagation(); 
           
    }).click(function () {
    
        // Confirm action - make sure that the user is sure:
        if(confirm('This widget will be removed, ok?')) {
        
            // Animate widget to an opacity of 0:
            $(this).parents(settings.widgetSelector).animate({
                opacity: 0    
            },function () {
            
                // When animation (opacity) has finished:
                // Wrap in DIV (explained below) and slide up:
                $(this).wrap('<div/>').parent().slideUp(function () {
                
                    // When sliding up has finished, remove widget from DOM:
                    $(this).remove();
                    
                });
            });
        }
        
        // Return false, prevent default action:
        return false;
        
    })
    
    // Now, append the new button to the widget handle:
    .appendTo($(settings.handleSelector, this));
    
}

EDIT:

/* (if "editable" option is TRUE) */
if (thisWidgetSettings.editable) {
    
    // Create new anchor element with class of 'edit':
    $('<a href="#" class="edit">EDIT</a>').mousedown(function (e) {
        
        // Stop event bubbling
        e.stopPropagation();
        
    }).toggle(function () {
        // Toggle: (1st state):
        
        // Change background image so the button now reads 'close edit':
        $(this).css({backgroundPosition: '-66px 0', width: '55px'})
            
            // Traverse to widget (list item):
            .parents(settings.widgetSelector)
                
                // Find the edit-box, show it, then focus <input/>:
                .find('.edit-box').show().find('input').focus();
                
        // Return false, prevent default action:
        return false;
        
    },function () {
        // Toggle: (2nd state):
        
        // Reset background and width (will default to CSS specified in StyleSheet):
        $(this).css({backgroundPosition: '', width: ''})
            
            // Traverse to widget (list item):
            .parents(settings.widgetSelector)
                
                // Find the edit-box and hide it:
                .find('.edit-box').hide();
        // Return false, prevent default action:
        return false;

    })
    
    // Append this button to the widget handle:
    .appendTo($(settings.handleSelector,this));
    
    // Add the actual editing section (edit-box):
    $('<div class="edit-box" style="display:none;"/>')
        .append('<ul><li class="item"><label>Change the title?</label><input value="' + $('h3',this).text() + '"/></li>')
        .append((function(){
            
            // Compile list of available colours:
            var colorList = '<li class="item"><label>Available colors:</label><ul class="colors">';
            
            // Loop through available colors - add a list item for each:
            $(thisWidgetSettings.colorClasses).each(function () {
                colorList += '<li class="' + this + '"/>';
            });
            
            // Return (to append function) the entire colour list:
            return colorList + '</ul>';
            
        })())
        
        // Finish off list:
        .append('</ul>')
        
        // Insert the edit-box below the widget handle:
        .insertAfter($(settings.handleSelector,this));
        
}

COLLAPSE:

// (if 'collapsible' option is TRUE) 
if (thisWidgetSettings.collapsible) {
    
    // Create new anchor with a class of 'collapse':
    $('<a href="#" class="collapse">COLLAPSE</a>').mousedown(function (e) {
        
        // Stop event bubbling:
        e.stopPropagation();
        

    }).toggle(function () {
        // Toggle: (1st State):
        
        // Change background (up-arrow to down-arrow):
        $(this).css({backgroundPosition: '-38px 0'})
        
            // Traverse to widget (list item):
            .parents(settings.widgetSelector)
                // Find content within widget and HIDE it:
                .find(settings.contentSelector).hide();
                
        // Return false, prevent default action:
        return false;
        
    },function () {
        // Toggle: (2nd State):
        
        // Change background (up-arrow to down-arrow):
        $(this).css({backgroundPosition: ''})
        
            // Traverse to widget (list item):
            .parents(settings.widgetSelector)
            
                // Find content within widget and SHOW it:
                .find(settings.contentSelector).show();
                
        // Return false, prevent default action:
        return false;
        
    })
    
    // Prepend that 'collapse' button to the widget's handle:
    .prependTo($(settings.handleSelector,this));
}

What is "Event Bubbling"?

Event bubbling or 'propagation' is when, upon clicking on an element, the event will bubble up through the DOM to the highest level element with an event the same as the event you just triggered on the original element. If we didn't stop propogation in the above snippets (e.stopPropagation();) on the mouseDown event of each added button then the mouseDown event of the handle (parent of the buttons) would also trigger and thus the dragging would would begin just by holding your mouse down over one of the buttons - we don't want this to happen; we only want dragging to begin when the user puts their mouse directly over the handle and pushes down.

Edit-box events/functionality

We've written the code which will inject the edit boxes into the document in the correct places. - We added an input box so users can change the title of a widget and we also added a list of available colours. So, we now need to loop through each new edit-box (hidden from view) and specify how these elements can be interacted with:

// Loop through each edit-box (under each widget that has an edit-box)
$('.edit-box').each(function () {
    
    // Assign a function to the onKeyUp event of the input:
    $('input',this).keyup(function () {
        
        // Traverse UP to widget and find the title, set text to
        // the input element's value - if the value is longer
        // than 20 characters then replace remainder characters
        // with an elipsis (...).
        $(this).parents(settings.widgetSelector).find('h3').text( $(this).val().length>20 ? $(this).val().substr(0,20)+'...' : $(this).val() );
        
    });
    
    // Assing a function to the Click event of each colour list-item:
    $('ul.colors li',this).click(function () {
        
        // Define colorStylePattern to match a class with prefix 'color-':
        var colorStylePattern = /\bcolor-[\w]{1,}\b/,
            
            // Define thisWidgetColorClass as the colour class of the widget:
            thisWidgetColorClass = $(this).parents(settings.widgetSelector).attr('class').match(colorStylePattern)
        // If a class matching the pattern does exist:
        if (thisWidgetColorClass) {
            
            // Traverse to widget:
            $(this).parents(settings.widgetSelector)
            
                // Remove the old colour class:
                .removeClass(thisWidgetColorClass[0])
                
                // Add new colour class (n.b. 'this' refers to clicked list item):
                .addClass($(this).attr('class').match(colorStylePattern)[0]);
                
        }
        
        // Return false, prevent default action:
        return false;
        
    });
});

The edit-boxes are entirely functional now. All the above code resides in the 'addWidgetControls' method.

addWidgetControls : function () {
    var iNettuts = this,
        $ = this.jQuery,
        settings = this.settings;
        
    $(settings.widgetSelector, $(settings.columns)).each(function () {
        var thisWidgetSettings = iNettuts.getWidgetSettings(this.id);
        
        if (thisWidgetSettings.removable) {
            $('<a href="#" class="remove">CLOSE</a>').mousedown(function (e) {
                e.stopPropagation();    
            }).click(function () {
                if(confirm('This widget will be removed, ok?')) {
                    $(this).parents(settings.widgetSelector).animate({
                        opacity: 0    
                    },function () {
                        $(this).wrap('<div/>').parent().slideUp(function () {
                            $(this).remove();
                        });
                    });
                }
                return false;
            }).appendTo($(settings.handleSelector, this));
        }
        
        if (thisWidgetSettings.editable) {
            $('<a href="#" class="edit">EDIT</a>').mousedown(function (e) {
                e.stopPropagation();    
            }).toggle(function () {
                $(this).css({backgroundPosition: '-66px 0', width: '55px'})
                    .parents(settings.widgetSelector)
                        .find('.edit-box').show().find('input').focus();
                return false;
            },function () {
                $(this).css({backgroundPosition: '', width: ''})
                    .parents(settings.widgetSelector)
                        .find('.edit-box').hide();
                return false;
            }).appendTo($(settings.handleSelector,this));
            $('<div class="edit-box" style="display:none;"/>')
                .append('<ul><li class="item"><label>Change the title?</label><input value="' + $('h3',this).text() + '"/></li>')
                .append((function(){
                    var colorList = '<li class="item"><label>Available colors:</label><ul class="colors">';
                    $(thisWidgetSettings.colorClasses).each(function () {
                        colorList += '<li class="' + this + '"/>';
                    });
                    return colorList + '</ul>';
                })())
                .append('</ul>')
                .insertAfter($(settings.handleSelector,this));
        }
        
        if (thisWidgetSettings.collapsible) {
            $('<a href="#" class="collapse">COLLAPSE</a>').mousedown(function (e) {
                e.stopPropagation();    
            }).toggle(function () {
                $(this).css({backgroundPosition: '-38px 0'})
                    .parents(settings.widgetSelector)
                        .find(settings.contentSelector).hide();
                return false;
            },function () {
                $(this).css({backgroundPosition: ''})
                    .parents(settings.widgetSelector)
                        .find(settings.contentSelector).show();
                return false;
            }).prependTo($(settings.handleSelector,this));
        }
    });
    
    $('.edit-box').each(function () {
        $('input',this).keyup(function () {
            $(this).parents(settings.widgetSelector).find('h3').text( $(this).val().length>20 ? $(this).val().substr(0,20)+'...' : $(this).val() );
        });
        $('ul.colors li',this).click(function () {
            
            var colorStylePattern = /\bcolor-[\w]{1,}\b/,
                thisWidgetColorClass = $(this).parents(settings.widgetSelector).attr('class').match(colorStylePattern)
            if (thisWidgetColorClass) {
                $(this).parents(settings.widgetSelector)
                    .removeClass(thisWidgetColorClass[0])
                    .addClass($(this).attr('class').match(colorStylePattern)[0]);
            }
            return false;
            
        });
    });
    
}

Almost finished

Now that we've written most of the JavaScript we can write the initiating method and intialise the script!

// Additional method within 'iNettuts' object:
init : function () {
	this.attachStylesheet('inettuts.js.css');
	this.addWidgetControls();
	this.makeSortable();
}

Now, to start it all:

// Right at the very end of inettuts.js
iNettuts.init();

Just so we're clear, this is the overall structure of our iNettuts object with each of its methods explained:

var iNettuts = {
    
    /* Set's jQuery identifier: */
    jQuery : $,
    
    settings : {
        
		/*    Name : settings
		 *    Type : Object
		 * Purpose : Object to store preferences for widget behaviour
		 */
		 
    },

    init : function () {
        
		/*    Name : init
		 *    Type : Function
		 * Purpose : Initialise methods to be run when page has loaded.
		 */
		 
    },
    
    getWidgetSettings : function (id) {
        
		/*      Name : getWidgetSettings
		 *      Type : Function
		 * Parameter : id of widget
		 *   Purpose : Get default and per-widget settings specified in 
		 *             the settings object and return a new object
		 *             combining the two, giving per-widget settings
		 *             precedence obviously.
		 */
		 
    },
    
    addWidgetControls : function () {
        
		/*    Name : settings
		 *    Type : Function
		 * Purpose : Adds controls (e.g. 'X' close button) to each widget.
		 */
		 
    },
    
    attachStylesheet : function (href) {
        
		/*      Name : settings
		 *      Type : Function
		 * Parameter : href location of stylesheet to be added
		 *   Purpose : Creates new link element with specified href and 
		 *             appends to <head>
		 */
		 
    },
    
    makeSortable : function () {
        
		/*    Name : settings
		 *    Type : Function
		 * Purpose : Makes widgets sortable (draggable/droppable) using
		 *           the jQuery UI 'sortable' module.
		 */
		 
    }
  
};

 Finished!

Finished Project

We're totally finished, the interface should be totally operable now. I've tested it on my PC (running Windows XP) in the following browsers: Firefox 2, Firefox 3, Opera 9.5, Safari 3, IE6, IE7 & Chrome.

Note: There are a couple of issues in IE. Specifically, it doesn't set the placeholder size correctly plus there are some CSS issues in IE6 (to be expected).

At first glance this interface's potential applications seem limited to those like iGoogle or NetVibes but it can, in fact, be used for many different things.

  • You could, for example use it on your blog by giving the user the option to sort your blog's widgets in the sidebar - you could then save their preference to a cookie so that the widgets would be in the same order when the user returns.
  • If you add a user authentication system and a database then you've got yourself a simple iGoogle.
  • The 'sortable' plugin itself can be used for sorting any elements, they don't have to be widgets.

Regardless of whether you're going to use this in a project or not I hope you've learnt something today!


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    Vlad November 18th

    Great Tut!!!

    Thanks

    ( Reply )
  2. PG

    Max November 18th

    Very impressive!
    You’ve made a lot of work

    ( Reply )
  3. PG

    Roshan Bhattarai November 18th

    Thanks…I had do make like facebook sortable in one project and I’ve used interface plugin(http://www.eyecon.ro/interface/) of jquery ……

    ( Reply )
  4. PG

    OpenSourceHunter November 18th

    Dammm this is really nice :D

    greetz
    http://www.opensourcehunter.com

    ( Reply )
  5. PG

    crysfel November 18th

    this is good :o

    ( Reply )
  6. PG

    Josh Drake November 18th

    Really, really nice tutorial! It is extremely interesting to see how Google does their work. Thanks!

    ( Reply )
  7. PG

    Guy November 18th

    very impressive.

    many thanks

    ( Reply )
  8. PG

    Jaswinder November 18th

    fracking excellent good work james all of your tuts are ones that i actually pay attention to….look forward to more

    ( Reply )
  9. PG

    Max November 18th

    Man this is cool, im going to try this later i think, nice one

    ( Reply )
  10. You are the man, great!!, thank you for share this knowledge with me a benginner!!!!

    ( Reply )
  11. PG

    _J November 18th

    Very nice and informative! Thanks

    JHAY

    ( Reply )
  12. PG

    Darren November 18th

    Brilliant!

    ( Reply )
  13. PG

    Craigsnedeker November 18th

    Slick!

    ( Reply )
  14. PG

    xQlusive November 18th

    Very nice tutorial! Thanx

    ( Reply )
  15. PG

    Shane November 18th

    Well done James – a great demo of what’s possible with jQuery UI!

    Thanks for writing it :)

    ( Reply )
  16. PG

    Eduardo November 18th

    when i think i’ve seen the best tutorial of nettuts, comes one that is even better!

    ( Reply )
  17. PG

    B McCarrie November 18th

    Nice work! And thanks.

    ( Reply )
  18. PG

    Unreal Media November 18th

    Brilliant tutorial. It behaves so smoothly :)

    ( Reply )
  19. PG

    Dan Harper November 18th

    Excellent work! Definitely one of the best tutorials here :D

    ( Reply )
  20. PG

    Neil November 18th

    Nice! What if you wanted to integrate this to a user homepage and store what widget is displayed in what placeholder in the database?

    ( Reply )
  21. PG

    Sergio Armando November 18th

    muy buen tutorial.

    ( Reply )
  22. PG

    Ervin Ter November 18th

    Nice stuff!

    ( Reply )
  23. PG

    Cristian Ciofu November 18th

    You become greater day by day.

    Congrats ;)

    ( Reply )
  24. PG

    insic November 18th

    another nice tutorial.

    ( Reply )
  25. PG

    James November 18th

    Thanks for all the great comments! I’m glad you all like it. :)

    Neil, you could quite easily make it possible for users to save their preferences in a database: When a change has been made to a widget you could fire off an Ajax request (posting all the changes) to a server-side script which does all the legwork.

    ( Reply )
  26. PG

    Ben Blogged November 18th

    Very very nice!

    ( Reply )
  27. PG

    Freddie November 18th

    Internet is my heaven! Nothing of all this in Italy… Thanks a lot! You’re a greater great!

    ( Reply )
  28. PG

    Vikram S. Haer November 18th

    Wow, this is slick. I have no idea of how to use javascript and am always amazed at the tutorials you guys offer. Now I’m really eager to get some learning done so I can experiment with this stuff.

    Thanks for the awesome tut. appreciate your hard work.

    ( Reply )
  29. PG

    Chris November 18th

    This is beyond excellent, there is so much potential. Imagine integrating API’s.

    Wonderful, just brilliant. Good work.

    ( Reply )
  30. PG

    Connor November 18th

    Nice Javascript James!

    ( Reply )
  31. PG

    Thomas Milburn November 18th

    Thanks for a brilliant tutorial. I’ve learnt a couple of new points. Much appreciated.

    Neil, you can also save the users preferences in a cookie which could be set without the need for any kind of server-side script.

    ( Reply )
  32. PG

    Ani November 18th

    Great stuff! Just what nettuts needed. Keep up the good work

    ( Reply )
  33. PG

    Miles Johnson November 18th

    One thing that stood out. IE6 does not support multiple inline classes like.

    .widget.red { } does not work but .widget .red {} does (notice the space).

    Damn nice tutorial, theres actually something similar Im working on, this might be helpful.

    ( Reply )
  34. PG

    Dicky November 18th

    Nice tutorial! I think maybe someone can make a Wordpress theme with this.

    ( Reply )
  35. PG

    Goodworks Media November 18th

    Man – fantastic. Best tutorial i think i’ve seen on this site yet.

    ( Reply )
  36. PG

    Blaz Arrowhead November 18th

    Sweet! I think I will whip out an iGoogle killer with this. It will be called iloveinfo.com. Nice work.

    ( Reply )
  37. PG

    Stefan November 18th

    Now I can build my own http://now.sprint.com/widget/ like site :D

    ( Reply )
  38. PG

    Joe McCann November 18th

    Wicked…well done…

    ( Reply )
  39. PG

    Paul Webb November 18th

    This was an excellent tutorial. Thanks for the link also Stefan, Sprint looks pretty cool now.

    ( Reply )
  40. PG

    Daniel P November 18th

    Great Work James, thank you very much for sharing this.

    ( Reply )
  41. PG

    Dzineblog November 18th

    It’s absolutely fantastic!!!!!

    ( Reply )
  42. PG

    Najam Sikander Awan November 18th

    hi
    very nice tutorial i loved it but can you post another tutorial in which you show how to save the layout modifications made by visitor? And then if visitor loads this page after browsing some more sites our page should load the modifications he made.

    Thanks

    ( Reply )
  43. PG

    Ariyo November 18th

    Thanks for a very informative tutorial. This is definitely one of the good ones here. Keep it up.

    ( Reply )
  44. PG

    Albert Araalie November 18th

    Wow! This is too cool!!

    ( Reply )
  45. PG

    Rijalul Fikri November 18th

    Thank you very much for this. I’ll surely use this someday for my own project.

    ( Reply )
  46. PG

    Ignas November 18th

    it’s great ;)

    ( Reply )
  47. PG

    Dharm November 18th

    fanatastic

    ( Reply )
  48. PG

    Lamin November 19th

    You are getting better and better at this James.. Keep it up.

    ( Reply )
  49. PG

    Diego November 19th

    great tutorial. i like to learn how to save. with jquery cookie

    ( Reply )
  50. PG

    mhd zaher ghaibeh November 19th

    its really nice

    ( Reply )
  51. PG

    Liam Dilley November 19th

    This looks a lot like how the bbc manage their site to me.

    ( Reply )
  52. PG

    HoyaSaxa93 November 19th

    Brilliant!… Just Brilliant!

    ( Reply )
  53. PG

    uhu November 19th

    very nice work.
    to be honest i have not read it all, but how do you store the personalized data?

    ( Reply )
  54. PG

    Andi November 19th

    Nice, but what’s about saving the positions until the next visit?

    ( Reply )
  55. PG

    Fabryz November 19th

    Nice work indeed, thanks =)

    ( Reply )
  56. PG

    Jean-Francois November 19th

    ouaaa thanks a looot

    ( Reply )
  57. PG

    aNieto2k November 19th

    Very nice, my aportation (sorry I’m spanish)

    function insertWidget(where, opt) {
    var selectorOld = iNettuts.settings.widgetSelector;
    iNettuts.settings.widgetSelector = '.new';
    salida = ''+opt.title+''+opt.content+'';
    $(where).append(salida);
    iNettuts.addWidgetControls();
    iNettuts.settings.widgetSelector = selectorOld;
    }
    //Sample
    insertWidget("#column1", {
    color: "blue",
    title: "Test",
    content: "This is a Test"
    });

    ( Reply )
  58. PG

    aNieto2k November 19th

    Sorry, HTML disapear

    function insertWidget(where, opt) {
    var selectorOld = iNettuts.settings.widgetSelector;
    iNettuts.settings.widgetSelector = '.new';
    salida = '<li class="new widget color-'+opt.color+'"><div class="widget-head"><h3>'+opt.title+'</h3></div><div class="widget-content"><p>'+opt.content+'</p></div></li>';
    $(where).append(salida);
    iNettuts.addWidgetControls();
    iNettuts.settings.widgetSelector = selectorOld;
    }
    insertWidget("#column1", {
    color: "blue",
    title: "Prueba",
    content: "ESTO ES UNA PRUEBA"
    });

    ( Reply )
    1. PG

      Marcela April 28th

      Don´t move!!!

      ( Reply )
    2. PG

      Sharon June 30th

      You can not darg the new Widget even if I add
      iNettuts.amakeSortable();

      ( Reply )
    3. PG

      bcmoney October 23rd

      You need to rebind the DOM, a common problem. Add the following to the bottom of the insertWidget method:

      iNettuts.init();

      The problem I found with this approach is that every time a new widget is added, the DOM gets re-binded, meaning that we end up with many EDIT CLOSE COLLAPSE boxes in the top header of the widget, and you can only successfully insert a max of 1 widget until things go wrong.

      The solution to this could be to change the code in iNetTuts.js to use a more intelligent check to determine whether it should perform “insertAfter” on the boxes or not… maybe we can use replaceWith if the DOM was already re-bound?

      Another approach might be Event Delegation:
      http://lab.distilldesign.com/event-delegation/

      If anyone thinks up another solution plz share with the group…

      ( Reply )
  59. PG

    Joel S November 19th

    Wow. This is intense. Thanks for the thorough tutorial!

    ( Reply )
  60. PG

    Diogo Duarte November 19th

    Really nice tut…
    Cool that this kind of tut is beggining to appear more…

    Congratulations!

    ( Reply )
  61. PG

    Jash Sayani November 19th

    Nice one…. Pretty amazing.

    However, when I re-opened the example, the places of the gadgets had changed back. How do I save the prefs in a cookie…?

    Thanks.

    ( Reply )
  62. PG

    Author November 19th

    Very amazing tut! I’ll definitely be reading through this in detail and learn quite a few new things.

    Like other readers, I also couldn’t help but wonder about saving the custom setting via cookie for each user.

    Cheers!

    ( Reply )
  63. PG

    frosted November 19th

    This is an amazing tutorial.. but it is one step away from reallyu being deployable..

    Could I request an extension of this tutorial that saves the positions in a cookie [:

    Thanks for taking the time to create this, I hope the community can make it usable “out of the box”

    [:

    ( Reply )
  64. PG

    vvvlad November 19th

    Great tutorial!!!
    I have much to learn…
    I found one small bug while in IE7 – the expand\collapse button is showing “edit” when used more then once.

    ( Reply )
  65. PG

    Dynamiclinq November 19th

    Wow very nice tut man! Thanks :) Could you please also show us how to save this in database or a cookie?

    ( Reply )
  66. PG

    Phil November 19th

    This is exactly what I need for part of an app I’m working on! Excellent work and thanks for going into great detail.

    ( Reply )
  67. PG

    Gustavo Macedo November 19th

    How about http://www.bbc.com/ ??? LOL

    ( Reply )
  68. PG

    FLO.master November 19th

    Wow, that’s what I need!!! =)

    ( Reply )
  69. PG

    Ahmet Alp Balkan November 19th

    Ah, nice and great tutorial, thanks James ;)

    ( Reply )
  70. PG

    James November 19th

    Thanks for all the awesome comments! :)

    There have been quite a lot of people asking about storing user preferences in a cookie. I’ve created a new (extended) version of iNettuts for those interested, you can find information here: http://james.padolsey.com/javascript/inettuts-with-cookies/

    @vvvlad, thanks! … fixed :)

    ( Reply )
  71. PG

    najamsk@gmail.com November 19th

    hi james
    i try your new demo but its not working for me after dragging any box if i try to drag second box it halts somewhere in middle of two columns and stays there and then one can’t drag anything so i have to refresh page :)

    Please test your demo on different machienes. I am using firefox 3.0.4.

    ( Reply )
  72. PG

    Diego November 19th

    i have a same problem with firefox 3.0.4.

    ( Reply )
  73. PG

    Anjum November 19th

    Fantastic…
    You are the best ….
    Go on …

    ( Reply )
  74. PG

    Sandeep November 19th

    wow…. nice tutorial….. thanks…..

    ( Reply )
  75. PG

    Swapnil Sarwe November 19th

    Thats a cool tutorial, james.
    thanx for sharing and guiding throughout. its a nice idea.
    and as per the extension with cookie, things are better.

    a bit of more imagination for the users who need this, and they can create miracles.
    a bit of ajax, for reading rss’s, flickr apis….
    hoosh i am already starting to think too much about it…

    ( Reply )
  76. PG

    Allan November 20th

    Very nice thanks!

    ( Reply )
  77. PG

    James November 20th

    @najamsk & Diego, are you still having problems with it?

    It’s odd because I haven’t really changed any of the core dragging functionality – I’ve just added a few lines of code so it will save the positions when you drop one of the widgets, nothing else has been changed.

    I think there may be some general performance issues with FF3, especially if you’ve had the browser open for a while. I was tying to use it yesterday with about 20 other tabs open and it was dead slow! (This may just be because I have so many extensions…)

    @Swawpnil – Exactly, the potential applications of this interface are endless, it just takes a bit of imagination! :)

    ( Reply )
  78. PG

    skyrocketlabs November 20th

    Fantastic work, James! This really opens up some creative possibilities.

    I’ve been playing with it and noticed that the cookie function isn’t working for me on FF, IE 6, or Chrome (on Win XP and Vista). I realize it’s a work in progress and your efforts thus far are already VERY much appreciated.

    Cheers ;)

    ( Reply )
  79. PG

    Issa Qandil November 20th

    This is really Amazing !!!!

    ( Reply )
  80. PG

    frisco November 20th

    I confirm the new demo is not working.
    It just seems to be just in the second endDrag, the javascript console is giving a “this.parentNode is null” error in jquery I tested both in IE7 and FF3 and the same happens.

    I suppouse it should be an error storing the cookie data when a drag is ended.

    ( Reply )
  81. PG

    NTulip November 20th

    consider to make a version which allows for embeded iframe content and store settings for the widgets.

    ( Reply )
  82. PG

    Eduardo November 20th

    @James
    It seems that your ‘cookied version’ is not working very well on my browsers. I’ve tested with Opera 9.61 and Firefox 2.0.0.18.
    There are some bugs besides the cookies.

    ( Reply )
  83. PG

    James November 20th

    @Everyone

    I was being really stupid. I left “console.log” in the cookie version which obviously made it fail in browsers without window['console'] … Anyway, it’s fixed now. I’ve tested it in Opera, Safari, FF2, FF3, IE6/7 and Chrome on my PC and it seems to work. Can someone please confirm this?

    Cookie version: http://james.padolsey.com/demo/tut-inettuts-with-cookies/
    Related blog post: http://james.padolsey.com/javascript/inettuts-with-cookies/

    ( Reply )
  84. PG

    Diego November 20th

    great job :)

    ( Reply )
  85. PG

    skyrocketlabs November 20th

    @James

    Yep, the cookies update looks good! Do you plan on making the cookie save collapsed states as well?

    GREAT job!

    ( Reply )
  86. PG

    James November 20th

    skyrocketlabs, Done! :) – The cookie saves collapsed states.

    ( Reply )
  87. PG

    skyrocketlabs November 20th

    Thanks, James! I applied the updated Javascript and the cookie file, but the strangest thing is happening…Everything loads but each widget has lost all of that cool functionality and the buttons.gif image has disappeared. So weird. I’m working on this locally, so unfortunately I don’t have a URL to share at the moment.

    ( Reply )
  88. PG

    skyrocketlabs November 20th

    I also get the following in Firebug:

    $.cookie is not a function

    Has anyone else experienced this?

    ( Reply )
  89. PG

    skyrocketlabs November 20th

    Scratch that. I figured it out.

    ( Reply )
  90. PG

    Diego November 20th

    skyrocketlabs
    the css “inettuts.js.css” have a new features, maybe you dont update this file
    “.collapsed .widget-content {display:none !important;}”

    ( Reply )
  91. PG

    skyrocketlabs November 20th

    @Diego:
    Actually, it was a conflict with the animatedcollapse.js file I was also using. I removed that and it’s working great!

    ( Reply )
  92. PG

    Sai Gudigundla November 20th

    That is some neat work James. Thanks for sharing it with us. Looking forward to more tutorials…

    ( Reply )
  93. PG

    freddy November 20th

    @ everyone:

    I’ve tried twice to post this and it didn’t take, so hopefully this is it…

    If you’d like to add a ‘reset’ function that allows users to delete the cookie and reload the interface defaults via a link (somewhat like on bbc.co.uk), refer to this quick tutorial I whipped up:

    http://www.skyrocketlabs.com/reset-function-to-inettuts-interface.txt

    Enjoy!

    ( Reply )
  94. PG

    David November 20th

    thanks for this!!

    ( Reply )
  95. PG

    James November 21st

    @Freddy, Nice work! … Instead of using ‘document.cookie’ you can use the jQuery Cookie plugin which I’m using in the new version of the script. (to delete a cookie: $.cookie('cookie-name',null))

    ( Reply )
  96. PG

    Nagaraj Hubli November 21st

    amazing!!

    ( Reply )
  97. PG

    freddy November 21st

    @James: thanks! And thanks for the tip! That’s less JS, to boot! :)

    ( Reply )
  98. PG

    Azizur Rahman November 21st

    An Excellent tutorial.

    I am going to try implement the backed to my next project.

    Thanks.

    ( Reply )
  99. PG

    ivo ivan November 21st

    Great work!

    ( Reply )
  100. PG

    Alex November 21st

    Great work! Any chance on updating the source.zip download with the cookie version?

    ( Reply )
  101. PG

    Taylor Satula November 21st

    Wow really good post guys

    ( Reply )
  102. PG

    catriel November 21st

    Hey I love this interface.
    Can this be saved in the data base also?

    ( Reply )
  103. PG

    Niels November 22nd

    I was looking for a tutorial like this for a while! I’m going to try it right now!

    And I have the same question as Catriel, how could it be saved (widget positions etc.)

    ( Reply )
  104. PG

    Jonathan Vargas November 22nd

    James, your work is excellent, thank you so much.

    I am trying to adjust it to allow multiple instances of the container box, so that I could have two or more DIVs (columns1, columns2, columnsN) each one allowing to move their inner widgets.
    ________
    | _ _ |
    | | | | | |
    | |_ | |_| |
    ————–
    ________
    | _ _ |
    | | | | | |
    | |_ | |_| |
    ————–

    What should be done to allow this behaviour with separated containers, on the same page?
    Thanks in advance.

    ( Reply )
  105. PG

    Jonathan Vargas November 22nd

    Ups, my text-design doesn’t look good. Wee they are just two separate containers where each one allows moving their inner widgets always into the same container, they can’t leave the container.

    ( Reply )
  106. PG

    kareem November 23rd

    this is wonderful tutorial i will put acopy of this lesson on
    my site here
    http://www.as7ap4you.com

    ( Reply )
  107. PG

    Peng November 23rd

    amazing length and attention to detail. thanks for commenting your code!

    ( Reply )
  108. PG

    Sridhar November 23rd

    Marvellous Jim. I love it

    ( Reply )
  109. PG

    kpjothivelu November 25th

    really superb!!!

    ( Reply )
  110. PG

    Rydgel November 25th

    Cool !

    ( Reply )
  111. PG

    Tom November 25th

    I modified this a bit to have 2 smaller columns (25%) on the left and right and 1 large column (50%) in the center. However, dragging a widget from the large column to one of the smaller columns works only sporadically. Anyone else encountered this problem?

    ( Reply )
  112. PG

    hari November 25th

    thanks

    and i need your help, how to get the preferences details to store it on a DB.

    ( Reply )
  113. PG

    Quim November 26th

    Very nice job @James!

    Just a thing: I’m having problems dragging when it’s necessary to move the dragging widget out of screen.
    I explain myself: if a widget is higher than the placeholder and the placeholder is at the bottom of the screen, I can’t move it over the placeholder because the widget can’t be out of the screen.

    Any solution?

    Thanks

    ( Reply )
  114. PG

    Kolia November 28th

    Hey,

    Great tutorial :) I think there are a few differences between the piece of codes you quote in this page and the source code, that made me some trouble :)

    Ex:
    In step 3, second piece of code: at bottom of HTML ocument, you call the JS files. I think inettuts.js should be called at last JS, after jquery-personalized, or it doesn’t work (my experience).

    Also in step 1, you wrote class=”widget red” but it should be color-red instead of red.

    Best regards, i go back to this piece of code, thanks for the good work!

    ( Reply )
  115. Amazing, Thanks!

    ( Reply )
  116. PG

    kareem November 30th

    this is wonderful topic …. i will put acopy of this topic on
    my site here
    http://www.as7ap4you.com

    ( Reply )
  117. PG

    Aleso December 2nd

    simply amazing, i will check it out this one

    http://www.3dand2dmag.wordpress.com

    ( Reply )
  118. PG

    MiriMiriChan December 3rd

    OHHH WOW!!! :D It’s very cool !! …I feel noob! xD hahaha

    ( Reply )
  119. PG

    bishal December 3rd

    Thanks very much for this marvellous script…..I needed similar script for my project and hell of time is saved with this one.

    ( Reply )
  120. PG

    Mauro December 4th

    This is great, but how to save the settings on the server? I have no idea how to do…

    ( Reply )
  121. PG

    Samuel Carlisle December 4th

    Fab tutorial, great contribution. Thank you.

    ( Reply )
  122. PG

    pieman December 4th

    Great work!

    I have used this one multiple pages on my site, problem is once on you change the position on one page, the other pages widgets totally disapear until you have deleted cookies from the brower. any solutions?

    ( Reply )
  123. PG

    Frans December 6th

    WOW! This script is perfect, and I was just looking for one of these! Thanks a lot, very cool man;)

    ( Reply )
  124. PG

    asdf December 6th

    Do you always ass your functions?

    Search your post for Assing!

    ( Reply )
  125. PG

    James December 10th

    Amazing work and nice tut! greetz

    ( Reply )
  126. PG

    graffiti December 10th

    Could be very useful with some exemples with php instead of cookies …
    and ajax system for recording…

    (give me the source of netvibes lol)

    ( Reply )
  127. PG

    David December 10th

    Hi, I worked on a jQuery plugin (Easy Widgets) inspired by your excellent tutorial. Maybe you want to view this plugin: please, visit this web page for more information. And in any case thanks a lot for your work James! ;)

    ( Reply )
  128. PG

    Ahmadi December 13th

    hi!
    how can i add a catalog view for add and remove somesections to colums at client side?
    i need to customization at client side such as saving and restoring state for clients like bbc.

    ( Reply )
  129. PG

    Lane December 16th

    Well done James!!!

    I am not very well-versed with the JQuery library, but I’m working on becoming familiar.

    I was wondering if you could perhaps share a code snippet example that would depict how to change the widget text color. I’m working with the cookies version of the inettuts code if that makes a difference.

    Thanks in advance and again, thanks for sharing this nice piece of code with us!!!!

    ( Reply )
  130. PG

    horton December 22nd

    is it possible to have an iframe open in the parent window and drag the widgets out of a child window and have them placed in the parent? if you could email me the answer or post a sub tutorial about it i would greatly appreciate it

    ( Reply )
  131. PG

    hoto December 23rd

    I notice that the collapse arrow doesn’t restore when you un-collapse a widget, you see a piece of the word edit instead.

    ( Reply )
  132. PG

    hoto December 23rd

    The collapse arrow doesn’t restore in IE6SP2

    ( Reply )
  133. PG

    Kunal December 25th

    Thanks a lot James… I needed to mimic the same thing in an windows application… and it really became a pain …

    Got a bit of ideas through this code … and that helped a lot…

    million thanks to you …

    ( Reply )
  134. PG

    December 26th

    非常好,

    ( Reply )
  135. PG

    官不聊生 December 27th

    你的大大的好的

    ( Reply )
  136. PG

    Smotsholle January 1st

    Great piece of code.
    I’ll be sure to use it…
    I’d like to implement this using AJAX.

    I have a few questions though:
    1. Is it easy to add new widgets using a home made menu?
    2. I noticed the top left widget is fixed. Can I make another item fixed? And can I sort other widgets around it without being able to move the fixed widget itself?

    ( Reply )
  137. PG

    Gerly Mieses January 4th

    Hi James,congratulations this tutorial is very cool,I download this code,I was see and try in my site demo,I see the handle can be moved to other positions but if the page is update (F5) the positions is reset,I want the positions are hold if the user move it.

    What can I do? thank you ahhh and Happy new year 09.

    ( Reply )
  138. PG

    frans January 5th

    thanks, james. great tutorial! a learn js a lot from this.

    one question, tho. i want to implement something like multiply.com where customization is only active in ‘customize mode’. how to achieve that?

    ( Reply )
  139. PG

    George January 6th

    Great tutorial!!!
    very nice interface!

    I tried to insert some php code in this example but every time i do this it doesn’t work! i can’t find the problem or with what it conflicts….

    The code i added was a simple RSS Feed code that works on it’s own just fine or in another HTML page i used it on!
    Any ideas?
    Thanks!

    ( Reply )
  140. PG

    Surjan January 7th

    very nice tutorials.
    I appreciate your efforts you put to make this effect
    thanks for such a nice article.

    ( Reply )
  141. PG

    jason cairns January 12th

    AMAZING TUTORIAL. I’m trying to do what George did. I was not able to get my simple RSS Feed working.

    Any Ideas?
    THANKS!

    ( Reply )
  142. PG

    ilker Aksu January 17th

    great.

    ( Reply )
  143. PG

    بشسيب January 18th

    شسيب يسبشس يبيسشبيسب

    ( Reply )
  144. PG

    Joe January 21st

    If running with Opera, the widget dragging will become slower and slower. Give it 10-20 minutes, you will see the slowing down.

    I tested it with all other browsers, IE, Safari, Chrom, Firefox, all good, but only Opera that it gets this problem.

    Any thought about the reason? Or any workaround?

    ( Reply )
  145. PG

    Kumar Bhogaraju January 21st

    Very cool and helpful. Thanks

    ( Reply )
  146. PG

    Imran January 21st

    Cool

    ( Reply )
  147. PG

    Adeel Shahid January 27th

    thanks for the great tutorial,

    just need to add display:block; to .widget-placeholder

    so that once the we move the widget the placeholder has the right size.

    ( Reply )
  148. PG

    Kris January 29th

    Damn… this is so f*** great – serached for sumthing like that for a long time. Keep up the good work!!!

    Big Ups
    Kris

    ( Reply )
  149. PG

    Sven January 29th

    You must be nuts! This is sooooo cool. Thanks for this tutorial!

    Cookie saving Version: What about saving the state ‘closed’ for a field also? the bbc site does it ;-)

    ( Reply )
  150. PG

    cheanizer January 30th

    thanks, it’s cool trick, and great tutorial.

    very helpful.

    ( Reply )
  151. PG

    Itzik Keidar February 1st

    ASTONISHING!
    Thank You!!!

    Can you tell me if the new source also include add the tabs that I deleted?

    ( Reply )
  152. PG

    Matt Froese February 3rd

    I love this plugin, however I have noticed one small thing.

    If you try to create a layout where all items are sortable (no introduction widget) the makeSortable function seems to fail when trying to return sortableItems:

    return $(’> li:not(’ + notSortable + ‘)’, settings.columns);

    I fixed this by simply checking if notSortable was empty. Probably not the most elegant solutions but it works for now.

    return notSortable != “” ? $(’> li:not(”‘ + notSortable + ‘”)’, settings.columns) : $(’> li’, settings.columns);

    ( Reply )
  153. PG

    Vincent February 3rd

    how do you create 4 columns, what do i have to edit in the code?

    ( Reply )
    1. PG

      avdhut May 25th

      hi i am also loioking for same have you got any solution

      ( Reply )
  154. PG

    Paul February 4th

    WOW! Thats coool. Good job James!!

    Paul

    ( Reply )
  155. PG

    naptime February 4th

    It does not work on my IE7 (all 3 of my computers)
    Does anybody know what wrong with my IE7 setup?

    - No round corner
    - Widgets are not movable

    ( Reply )
  156. PG

    naptime February 5th

    IE7 – I find out what the problem is. (Must have script enable).
    After uninstalling IE8, IE7 disable script automatically.

    ( Reply )
  157. PG

    niik February 5th

    Fantastic implementation. Thanks a bunch! I am trying to work out some bugs with IE when displaying the widgets in an iframe.

    @hoto:

    “The collapse arrow doesn’t restore in IE6SP2″

    Change the lines in inettuts.js to the following:

    if (thisWidgetSettings.collapsible) {
    $(’COLLAPSE‘).mousedown(function (e) {
    e.stopPropagation();
    }).toggle(function () {
    $(this).css({backgroundPosition: ‘-38px 0′})
    .parents(settings.widgetSelector)
    .find(settings.contentSelector).hide();
    return false;
    },function () {
    $(this).css({backgroundPosition: ‘-52px 0′})
    .parents(settings.widgetSelector)
    .find(settings.contentSelector).show();
    return false;
    }).prependTo($(settings.handleSelector,this));
    }

    The ‘-52px 0′ part is missing in the original script.

    ( Reply )
  158. PG

    kob February 6th

    Good job. Really smooth.
    I want to save it to mysql in array type.
    Can you show me how to alert items in each column?

    ( Reply )
  159. PG

    Tamer Qtaish February 12th

    WOW, Very impressive indeed! I solute you :D
    I was wondering tho! Is there an easy way to add more options to the edit block, or should I just write it into the library my self…

    What I mean to say is that what if I want to change for example the size as well?

    Just me wondering,
    Best regards,
    Tamer Qtaish

    ( Reply )
  160. PG

    Noboyband February 12th

    Very Very impressive and cool.
    I will be fighting same u

    ( Reply )
  161. PG

    hekkie (the netherlands) February 13th

    anyone knows how to put the credentials into and get it out of a DataBase? I do like the cookie one, but i want to create an “admin” version where they can sort the widgets, but “customers” can’t only see the view of it…

    Thanks for this great script, great comments and good lazy programming!!!

    keep up the good work

    ( Reply )
  162. PG

    Cazorla February 13th

    Hello from Spain James! This is perfect! Can yo do a theme for wordpress with this code? Can you tell us how to do it?

    This will be impresionant! Thanks!

    ( Reply )
  163. PG

    Levin February 17th

    I like it man!!

    ( Reply )
  164. PG

    yasmez February 24th

    I use Authentification user and i don’t knew how to save preferences of widgets in databases can you help me and give me any idee please

    Thx

    ( Reply )
  165. PG

    SUJITH PV February 26th

    Hi James

    Very nice article.Really interesting stuff,well explained in a very simple manner.
    Great article.

    ( Reply )
  166. PG

    wangchang February 27th

    It’s very good!thanks!

    ( Reply )
  167. PG

    Pranav March 2nd

    Thanks! It’s just awesome!!

    ( Reply )
  168. PG

    kandar March 3rd

    Great tutorial. I’ve been searching like this for so many time… Great Works!!

    ( Reply )
  169. PG

    kandar March 5th

    @James and @all,

    I modified this script so it can works with php.
    Insteed using Javascript to create cookie, I prefer using PHP to do that.
    I used Ajax method to heandle this proccess.
    I hope this API could make more easy for those who wan to store in DB and any server-side prossess.

    You can download the source code here:

    http://www.kandar.info/dt/inettuts_with_php.tar.gz

    thx for your great works james.

    ( Reply )
    1. PG

      Khan August 25th

      Dear
      I try this, its working fine in Firefox but not working in internet explorer 7, can you please tell me whats problem
      Thanks

      ( Reply )
  170. PG

    Benjamin March 6th

    Great stuff James! I love this tutorial.

    This JavaScript can run it in a light weight, that make a good performance.

    I have searched some similar DnD iGoogle liked UI before, most of them are server side coding and heavy loading JS script.

    ( Reply )
  171. PG

    MegaPHASE March 8th

    Simply amazing! This runs extremely fast! other examples of this I saw were less robust and lagged alot

    ( Reply )
  172. PG

    Vivek March 9th

    that’s great, i m going to use it in my application…
    Thanks

    ( Reply )
  173. PG

    James March 12th

    Is there a way I can get the browser to remember where the boxes were moved to?

    Ideally I’d like to store this in a database so that the correct layout was shown when a user logs in.

    ( Reply )
  174. PG

    advise-art March 12th

    amazing … wowww

    ( Reply )
  175. PG

    shijin March 14th

    Hello

    Great .

    I have a doubt , if I want to add one Ajax query while we clicking on the the delete button(remove class).

    ( Reply )
  176. PG

    Rob March 14th

    Wish we could have an example with using a database! That would be amazing!

    ( Reply )
  177. PG

    Chris March 18th

    Does anyone have a way to get this to work with the latest version of jQuery 1.3.x ?

    ( Reply )
  178. PG

    thomas March 26th

    great work!

    ( Reply )
  179. PG

    Aaron March 30th

    This is excellent thanks. One slight issue I found is that if you have no fixed widgets then on inettuts.js line 151:

    return $(’> li:not(’ + notSortable + ‘)’, settings.columns);

    the notSortable variable is empty, resulting in a jQuery: $(’> li:not()’) , which fails.

    I’ve altered to be:

    var selector = ‘> li’;
    if(notSortable && notSortable != ”)
    selector = ‘> li:not(’ + notSortable + ‘)’;
    return $(selector, settings.columns);

    ( Reply )
    1. PG

      Jason Buberel May 9th

      Just ran into this problem myself and was trying to walk through the debugger – thank you!!

      ( Reply )
    2. PG

      Doug Dimick September 14th

      My thanks as well, was trying to sort out this exact problem.

      ( Reply )
  180. PG

    KJFG March 30th

    Excelent Turorial, great job! Congratulations form Venezuela

    ( Reply )
  181. PG

    孔令一 April 1st

    非常不错,谢谢。

    ( Reply )
  182. PG

    mg April 3rd

    Has anyone noticed a bug when trying to drop an element?
    For example when we want to drop an element at the bottom and we approach from below.
    It’s very hard to put an element at the end – most of times it goes above the last element

    ( Reply )
    1. PG

      mg April 3rd

      Am I right in assuming that this is a bug in jQuery UI?

      Thanks for this great tutorial.

      Cheers!

      ( Reply )
      1. PG

        mh April 6th

        I have the same problem. How to fix this?

  183. PG

    Ash April 4th

    Hey this is a great tutorial and script! Just one question however, How can i make it save the location of the windows in a mysql database?

    ( Reply )
  184. PG

    Lars G. Skjellerup April 6th

    Great tut. I will be trying out that stuff on the next project I start.

    ( Reply )
  185. PG

    ken April 10th

    if I want to save this date in database.how to do!
    Can you give me a simple ensample!

    ( Reply )
  186. PG

    Kaiser April 12th

    Note for use with jquery 1.3.2.
    Widget will have moving width on mouseup() event in makeSortable() function.
    So replace this code :

    if(!$(this).parent().hasClass(’dragging’)) {
    // Patch for jquery.1.3.2
    //$(this).parent().css({width:”});
    } else {
    $(settings.columns).sortable(’disable’);
    }

    in

    if($(this).parent().hasClass(’dragging’)) {
    $(settings.columns).sortable(’disable’);
    }

    Great job thanks to you from France.

    ( Reply )
  187. PG

    Ali April 12th

    Nice. Great tutorial….exacly what I was looking for.

    thanks a lot man and good luck with future works

    ( Reply )
  188. PG

    Nhut April 14th

    That great,
    May i use this plugin for comercial product?

    nhutnguyenhong [at] gmail.com
    VietNam.

    ( Reply )
  189. PG

    Dalibor Sojic April 15th

    The missing part is to “save” order into cookie (to “know” order for next viewing)

    ( Reply )
  190. PG

    Me April 15th

    Many thanks for this James although I totally agree with the last comment – this code is not complete if it does not remember what you have done (like google or the bbc etc)

    ( Reply )
  191. PG

    Bazmo April 21st

    To all those ingrates who say: “what about the save option?”
    Especially the tool above.

    Get farked!

    That’s like saying to Enzo Ferrari: “What about the garage?”

    Build it yourself!

    ( Reply )
  192. PG

    Rui Costa April 22nd

    That is simply a great tutorial and it was a helpfull guide to me but i have a issue about this code, i need to add widgets with ajax. I already solved the remove button in the inserted widget but i can’t make the widget sortable.
    Anyone have an idea of how to accomplish this?

    ( Reply )
  193. PG

    tony April 27th

    it’s good.
    but i have a question. if i want to get the rowIndex and columnIndex of ecach widget, and store them to my datebase.

    i have read your code , i dont find how to get the rowindex and columnindex, i am beginner for jquery, but my project is starting.
    so i hope you can give me a opinion to help me.
    thanks very much
    email:lianluck_929@hotmail.com

    ( Reply )
  194. PG

    peter May 3rd

    Can anybody show the saving part, save on cookies and database (for logged in user) please?

    ( Reply )
  195. PG

    CgBaran Tuts May 6th

    Great tutorial thanks

    ( Reply )
  196. PG

    Guillaume May 7th

    Very great tut!

    I’ve designed a netvibes-like web site, but i didn’t come up with such well coded methods!

    I wanted to develop a class like this but didn’t know where to start!

    This will be very useful thank you !

    ( Reply )
  197. PG

    LP May 12th

    estupendo, lo maximo!!!!, muy bueno felicitaciones, muy buen trabajo!!

    ( Reply )
  198. Here’s a fix for jquery-ui-1.7.1 & jquery-1.3.2
    REPLACE
    var notSortable = ”;
    WITH
    var notSortable = ‘#none,’;

    Without the fix, you will not be able to move any widget around unless you have an ‘id’ associated with at least one widget.

    PS: If anyone has a better solution, please post! Thx!

    ( Reply )
  199. PG

    chaitanya May 19th

    This is awesome :-)

    ( Reply )
  200. PG

    avdhut May 24th

    This is cool .
    Can you tell me how to increase the columns

    ( Reply )
  201. PG

    avdhut May 25th

    hi can you tell me how to add columns to it.

    ( Reply )
  202. PG

    Joel May 25th

    Thanks for the effort, I will try this out.

    ( Reply )
  203. PG

    avdhut May 25th

    hi cool code i have been looking for this
    but i have not been able to increase the columns if i can increase the columns it will be greate

    ( Reply )
  204. PG

    Delli May 25th

    great example have just a tiny problem trying to run it with build with last version but face some problems may be u add more than “sortable” and “draggable” modules?

    ( Reply )
  205. PG

    Mahbub May 29th

    Looks neat James. I have worked with the one from the interface libraries. Your one looks good too. Just thought if there were any ways from your script to serialize all the layers along with their properties. Of course i can iterate through them using DOM. But if you have some better ideas, you it would be more useful.

    ( Reply )
  206. PG

    Ramana Rao June 3rd

    James, I am using this code in my next project. This one is great and really usefull

    ( Reply )
  207. PG

    wpdigger June 3rd

    Very impressive!You’ve made a lot of work. Thanks

    ( Reply )
  208. PG

    avdhut June 4th

    Nice tutorial
    i am using ur code in my porject but i came across a problem
    The widget is not properly defining the images here either the color of border comes as background of image or image is displayed but its size is more than the widget size and i looks awfull. I am having problem with all kind of images.

    ( Reply )
  209. PG

    bear_puppet June 7th

    so cool, i just like it th x

    ( Reply )
  210. PG

    Steven Fleet June 11th

    James, thank you so much for a great tutorial. I’ve been looking for a god expandable dashboard for quite a while. I’ve added a flickr widget using a plugin and a google ajax widget. The options are endless.

    ( Reply )
  211. PG

    yannux June 20th

    Thanks for this AWESOME tutorial !!!

    Need this to build tool for end user to customize site layout’s block position from administration panel

    Thanks !!!!

    ( Reply )
  212. PG

    guu June 21st

    goodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd

    ( Reply )
  213. PG

    apo June 24th

    hellow can i use this code for commercial use ?

    ( Reply )
  214. PG

    matstone99 June 26th

    Is there any way I can load up a page with some of the widgets collapsed?

    ( Reply )
  215. PG

    Vicky June 30th

    hi, nice utility, but how can i serialize the sortables

    ( Reply )
  216. PG

    Atif Zia July 1st

    its great module. thanks for this work.

    regards,
    Atif Zia

    ( Reply )
  217. PG

    Jerome July 6th

    James, great tut!!! I love it.

    I used the database version of Kandar instead of cookies, but had problem with the saving of the items. It saved the old layout after dropping a widget to a new location.

    I fixed this by adding a line in the javascript:
    change:

    }).mouseup(function () {
    if(!$(this).parent().hasClass(’dragging’)) {
    $(this).parent().css({width:”});
    } else {
    $(settings.columns).sortable(’disable’);
    }
    });

    in the following:
    }).mouseup(function () {
    if(!$(this).parent().hasClass(’dragging’)) {
    $(this).parent().css({width:”});
    } else {
    $(settings.columns).sortable(’disable’);
    }
    setTimeout(”iNettuts.savePreferences();”, 500);
    });

    ( Reply )
  218. PG

    Krish July 8th

    Hi,

    First wonderful interface really appreciate, I m trying to put a JqueryUI accordion/Tabs inside one of the portlet, I get only plain text links and no effects.

    Please advise this new-bee.

    Regards
    Krish

    ( Reply )
  219. PG

    Paaaaa July 8th

    Nice tutorial. Is it possible to also have full width tabs?
    like this:
    |||||||||||||||||||||

    |||||| |||||| ||||||

    |||||| |||||| ||||||

    |||||||||||||||||||||

    |||||||||||||||||||||

    |||||| |||||| ||||||

    Thank You

    ( Reply )
  220. PG

    Vin July 16th

    u a the man…Thanks for the excellent work…

    Just curious. Is there any other technologies that can do the same thing??? It seems tot me..JavaScript is the only option…pls advise…

    ( Reply )
  221. PG

    matstone99 July 20th

    If anyone can offer any advice on how I can load up this interface with one or more of the widgets collapsed it would be much appriciated. I’ve tried writing some of my own scripts to do it but nothing works effectively. my e-mail is matstone99@gmail.com if anyone wishes to help :)

    ( Reply )
  222. PG

    sawan July 21st

    This is gr8 work, I must say.

    However, trying to make it work with JQuery 1.3 version but it did not work. Any clues?

    ( Reply )
  223. PG

    fred July 21st

    Sorry about that, but could someone give me a clue to make the widget sizable….? I know, stupid question for guy able to write such tuts…But any help will be appreciated!

    ( Reply )
  224. PG

    Neehal July 24th

    I am not able to drag the widgets around.
    The scripts that I am using are the same as the ones used in the tutorial.
    I have placed all scripts and styles in the respective folders in my web project and have also changed the paths in the concerned scripts and the jsp as well.
    Any help would be a blessing.

    ( Reply )
  225. PG

    Neehal July 24th

    Hey guys !!!
    The tutorial works fine for me now. I deleted the old project and started afresh. That’s all.
    Thanx anyways.
    PEACE. :)

    ( Reply )
  226. PG

    thongd August 4th

    Very nice!

    ( Reply )
  227. PG

    Kannabiran August 7th

    Nice One.good stuff

    ( Reply )
  228. PG

    Chary August 10th

    Hey James,

    Nice one man. Keep it up. How can we add a widget to a page like we can see in IGoogle?

    ( Reply )
  229. PG

    Guille August 10th

    Very nice.

    I’m currently working on an iNetTuts xAJAX framework.
    I’ve got the add-widget part down.
    Currently working on showing/hiding (minimize/edit/close) icons based on a classname in stead of the ID.

    Thanks for this component. I’m glad the source code is readable and that it’s based on jQuery in stead of mootools or prototype.

    ( Reply )
    1. PG

      Damian September 9th

      Mind sharing how you did the add-widget please ?

      ( Reply )
  230. PG

    Lionel August 11th

    Hi,

    Thanks for that script. I implemented it with jQuery tabs where each tab makes an ajax call to a different page with different widgets. However the cookie is giving me a hard time when I switch tab. It throws me an error
    cookie.split(”|”)[i] is undefined

    How can I include the tab id in the cookie? Thank you.

    ( Reply )
    1. PG

      Neil September 3rd

      Did you get this one figured out?

      ( Reply )
  231. PG

    AMIT LAKHTARIA August 13th

    Thanks a lot… It’s an AMAZING TUTORIAL

    ( Reply )
  232. PG

    rat32 August 14th

    wow
    its informative

    ( Reply )
  233. PG

    Eman August 19th

    Wow, you put a lot of work into this. Good job and thanks for sharing!

    ( Reply )
  234. PG

    Jignesh Panchal August 21st

    I’m using this jquery with ASP.Net UpdatePanel,

    on first page load it’s work fine, but after asynchronous request, it doesn’t work.

    Please help me.

    ( Reply )
  235. PG

    Rakesh kumar August 26th

    Hi
    very very nice work done.

    I want one thing that there should be a full screen button on widget handle and as i click it widget should be full screen on complete page and in toggle it should be in its original size.

    can you please send me the solution. That’ll be very appreciable.

    Thanks

    ( Reply )
  236. PG

    Wetals August 29th

    WOW….this is great and am loving it.
    I just used this tutorial to create a test project – http://www.codefusionng.com/ondo

    ( Reply )
  237. PG

    Hector August 30th

    James, can we do one enhancement and make pane resizable.

    ( Reply )
  238. PG

    Snader September 1st

    Very nice! Thanks for this!

    I made a page with 2 columns and started to use images for the background of the panes. There is one problem though:

    In IE there is a 3px difference between the header and the content. This isn’t the case in Firefox and Chrome. But when dragging and dropping a pane in IE, the 3px difference is gone. See http://www.ballenbak.net/test/ for an example.

    ( Reply )
  239. PG

    DalSoft September 2nd

    Good work… I am interested in creating a OS rails back-end for this but is your code am open source license?

    ( Reply )
  240. PG

    Shane Dowling September 3rd

    Hey,

    Has anyone had issues with flash?
    Whenever I use flash within these widgets and try to drag and drop or delete them, the page refreshes and displays only the flash content.

    ( Reply )
    1. PG

      Mat October 1st

      Have you figured out how to fix this. It seems that after a box is moved it reload it.

      ( Reply )
  241. PG

    Tommy September 10th

    Hi Guys,

    Cracking tutorial, does anyone know how to get the widgets to display collapsed as oppose to showing the content when the page first loads???

    Help much appreciated

    ( Reply )
  242. PG

    sen September 11th

    Excellent Work…

    ( Reply )
  243. PG

    alphonse September 14th

    Great Tutorial.. like all the others…

    Keep it up.. I am actually learning somethings in this site… thanks…

    ( Reply )
  244. PG

    Mario September 19th

    Hi. I have made an example using a database. I have published it in my blog. The blog is in Spanish but i have writen this post in English (sorry for my poor English).

    http://jsabino.wordpress.com/2009/09/19/how-to-mimic-the-igoogle-interface-with-database/

    Thank you James. Your contribution is really great.

    ( Reply )
  245. PG

    Igor September 23rd

    Pretty cool! What is the license for this technology? Can we use it in production?

    ( Reply )
  246. PG

    pokiri September 24th

    Hi James,

    This is really simple and nice, But I want layout customization.
    i.e., I need two column layout. so how to do this one.

    Thanks in Advance

    ( Reply )
  247. PG

    Nazir October 5th

    Thank you, a lot! God Bless You!

    ( Reply )
  248. PG

    zzk October 7th

    perfect tutorial..

    ( Reply )
  249. PG

    Joe October 11th

    Pretty Awsome!

    ( Reply )
  250. PG

    Heba October 18th

    Just wanted to ask if someone has tried merging blueprint css with inettuts.

    Would really appreciate the help.

    ( Reply )
  251. PG

    Anders Tillebeck October 21st

    Thanks! Great tutorial.

    Only one thing to the wish list…
    How to reinsert the elements that the user removed (seen as dropdown on other pages).

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    October 21st