hello world! (sounds familiar..)
lr1 parser is complete! woooah. it takes me 3 weeks of work and now i will have a printed permanent smile on my face for at least 1 month. well i will post source codes, but not for now :).
today i’d like to show you a nice work made with jquery, a powerful javascript library that i discovered two days ago.
This is a tutorial that will show how to make a simple but nice customizable blocks-made interface/homepage, with jquery. the script is based on this other tutorial.
Here it is the demo: http://www.valerioriva.it/jquery/lotti-block.html
how to use the demo: you will see 6 blocks. click on the wrench to enter customization mode, a dialog will appear. click on every boxes’ icon (click and drag to move blocks with 4 arrow cross) and customize your layout. save or restore states/order/everything if you don’t like the customization.
features:
- movable blocks
- closable blocks
- minimizable blocks
- save blocks’ state and order into cookies
let’s start with the html template.
build your html layout.. with divs! made 1 or 2 or 3 or how many you need div “columns”. i’ll choose 3. then, you have to put inside each columns some divs that will be acting as boxes with some simply css commands.
here it is my example [remember to include always jquery library, jquery cookie plugin, jquery ui theme script and of course, my script (lotti-block)]
there is also a css example for people that doesn’t know how to make divs act as blocks
<html> <head><title>Demo</title> <link type="text/css" href="css/demo/jquery-ui-1.7.2.demo.css" rel="stylesheet" /> <script type="text/javascript" src="js/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="js/jquery.cookie.js"></script> <script type="text/javascript" src="js/jquery-ui-1.7.2.demo.min.js"></script> <script type="text/javascript" src="js/lotti-block.js"></script> <style> #left { width: 20%; float:left; } #center { width: 58%; float: left; margin-right:1%; margin-left:1%; } #right { width: 20%; float:right; } .box { background-color:#eef3f8; border:1px solid #d5dde5; border-bottom:4px solid #d5dde5; padding:10px; margin-bottom:15px; overflow:hidden; } .box h3{ background:#d5dde5; color:#1d3652 } .portlet-header .ui-icon { float: right; } .placeholder { border: 1px dotted black; visibility: visible !important; background-color: #0066CC; color: #FFFFFF; font-weight: bold; } .placeholder * { visibility: hidden; } .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; height: 100px !important; } .ui-sortable-placeholder * { visibility: hidden; } </style> </head> <body> <div id="customize-dialog" title="Reset Layout Page"> <p>Now you can customize your page.<br /> Move boxes from column to another, minimize/close them or get back to default settings by clicking on these buttons:</p> </div> <div id="left"> <div align="center">PLACEHOLDER</div> <div id="b1"><h3>Block 1</h3> <div>Block 1</div> </div> <div id="b2"><h3>Block 2</h3> <div>Block 2</div> </div> </div> <div id="center" > <div align="center">PLACEHOLDER</div> <div id="b3"><h3>Block 3</h3> <div>Block 3</div> </div> <div id="b4"><h3>Block 4</h3> <div>Block 4</div> </div> </div> <div id="right" > <div align="center">PLACEHOLDER</div> <div id="b5"><h3>Block 5</h3> <div>Block 5</div> </div> <div id="b6"><h3>Block 6</h3> <div>Block 6</div> </div> </div> </body> </html>
ok. now let’s examine this page. every column (they have IDs: left, center and right) must have the class identifier “sortable” and each block must have a different ID (in this example from b1 to b6). then placeholder divs (class=”placeholder”) blocks are used only to facilitate boxes drag’n’drop inside each column.
Every box have some special class that are: customizer, movable, minimizable, closable. each of those words adds an ability to the block. There must be at least 1 customizer (and preferably it doesn’t have to be closeable). you can use every combinations of those class identifier.
Then, a box is defined, as you can see, by this code (for example, i took the last block)
<div id="b3"><h3>Block 3</h3> <div>Block 3</div> </div>
REMEMBER! there must be a tag with class identifier “portlet-header” (i used h3 but it could be a div too) and another tag (preferably a div) with class identifier “portlet-content”
there is a “dialog” div too (it will act as a dialog box) that will appear when the customizer icon is clicked. it contains buttons to save or restore the homepage configuration/style.
now let’s see something about the js code
//configurable variables var cookiename='demo'; var cookie_options = { path: '/', expires: 10 }; function resetCookie() //delete cookie { jQuery.cookie(cookiename, null, cookie_options); } function resetCookieState() //delete only blocks' states from cookie { var cookie = jQuery.cookie(cookiename); if (!cookie) return; var cookie=cookie.split("|"); var savedBlocks = cookie[0]; var savedState = cookie[1]; jQuery.cookie(cookiename, savedBlocks+'|', cookie_options); } function resetCookieOrder() //delete only blocks' order from cookie { var cookie = jQuery.cookie(cookiename); if (!cookie) return; var cookie=cookie.split("|"); var savedBlocks = cookie[0]; var savedState = cookie[1]; jQuery.cookie(cookiename, '|'+savedState, cookie_options); } function getCookie() //read the cookie and restore blocks' order and states { var cookie = jQuery.cookie(cookiename); if (!cookie) return; var cookie=cookie.split("|"); var savedBlocks; var savedStates; if (cookie[0]) savedBlocks = cookie[0]; if (cookie[1]) savedStates = cookie[1]; //order if (savedBlocks) { var orders = savedBlocks.split(";"); //below you need to be replace with your columns id!! jQuery("#left").sortable('toArray'); jQuery("#center").sortable('toArray'); jQuery("#right").sortable('toArray'); //here too!! if(orders[0]) restoreOrder('#left',orders[0]); if(orders[1]) restoreOrder('#center',orders[1]); if(orders[2]) restoreOrder('#right',orders[2]); } //states if (savedStates) { var states = savedStates.split(","); var blocks_id = new Array(); var blocks_state = new Array(); var i=0; for (i=0; i<states.length; i++) { var temp = states[i].split("="); blocks_id[i]=temp[0]; blocks_state[i]=temp[1]; } for (i=0; i<states.length; i++) { var item=blocks_id[i]; var state=blocks_state[i]; if (state==1) jQuery("#"+item).find(".portlet-content").hide(); else if (state==2) jQuery("#"+item).hide(); } } } function setCookie() //save the blocks' order and states inside a single cookie { var s=""; var i=0; var n=0; //order //below you need to be replace with your columns id!! if (jQuery("#left").sortable('toArray')!="undefined") s+=jQuery("#left").sortable('toArray')+";" if (jQuery("#center").sortable('toArray')!="undefined") s+=jQuery("#center").sortable('toArray')+";" if (jQuery("#right").sortable('toArray')!="undefined") s+=jQuery("#right").sortable('toArray')+";" s=s.substr(0,s.length-1); s+='|'; //states var blocks_minimized = new Array(); var blocks_closed = new Array(); n=jQuery('.portlet-content:hidden').size(); for (i=0; i<n; i++) s+=jQuery('.portlet-content:hidden').eq(i).parent('.minimizable').attr('id')+"=1,"; n=jQuery('.closable:hidden').size(); for (i=0; i<n; i++) s+=jQuery('.closable:hidden').eq(i).attr('id')+"=2,"; if (n!=0) s=s.substr(0,s.length-1); if (s.length>0) jQuery.cookie(cookiename, s, cookie_options); else jQuery.cookie(cookiename, null); } function restoreOrder(list,order) //restore blocks' order for each column { var list = jQuery(list); if (list == null) return // make array from saved order var IDs = order.split(","); for (var i = 0, n = IDs.length; i<n; i++) { var item = IDs[i]; // select the item from the proper column var child = jQuery("div.ui-sortable").children("#" + item); // make a copy of the item var savedOrd = jQuery("div.ui-sortable").children("#" + item); // remove the original item child.remove(); //insert the copy inside the ordered column jQuery(list).append(savedOrd); } } function removeButtons() //remove cutomization buttons { jQuery(".ui-icon-newwin").replaceWith(''); jQuery(".ui-icon-arrow-4").replaceWith(''); jQuery(".ui-icon-power").replaceWith(''); } function addButtons() //adds customization buttons and actions { removeButtons(); jQuery(".minimizable").find(".portlet-header").append('<span class="ui-icon ui-icon-newwin"></span>') jQuery(".movable").find(".portlet-header").append('<span class="ui-icon ui-icon-arrow-4"></span>') jQuery(".closable").find(".portlet-header").append('<span class="ui-icon ui-icon-power"></span>') jQuery(".minimizable").find(".portlet-header").find(".ui-icon-newwin").click(function() { jQuery(this).parents(".minimizable").find(".portlet-content").toggle(); }); jQuery(".closable").find(".portlet-header").find(".ui-icon-power").click(function() { jQuery(this).parents(".closable").hide(); }); } function enableCustomization() //shows the dialog, the placeholders, makes columns item sortable and adds buttons { jQuery('#customize-dialog').dialog('open'); jQuery(".placeholder").show(); jQuery(".sortable").sortable({ handle: '.ui-icon-arrow-4', tolerance: 'pointer', items: '.movable', connectWith: '.sortable', update : function () { setCookie(); } }); addButtons(); } function disableCustomization() //remove buttons, set the cookie, hides the placeholders and block the columns { removeButtons(); setCookie(); jQuery(".placeholder").hide(); jQuery(".sortable").sortable('destroy'); } jQuery(function() { //jQuery "Main" jQuery('#customize-dialog').dialog({ //configure the dialog box autoOpen: false, width: 400, buttons: { "Everything": function() { //reset to normal page layout jQuery(this).dialog("close"); //close the dialog resetCookie(); //full reset window.location.reload(); //refresh page to see changes }, "States": function() { //reset only the states jQuery(this).dialog("close"); //close the dialog resetCookieState(); //partial reset, only states window.location.reload(); //refresh page to see changes }, "Order": function() { //reset only the order jQuery(this).dialog("close"); //close the dialog resetCookieOrder(); //partial reset, only order window.location.reload(); //refresh page to see changes }, "Save": function() { //save state and order jQuery(this).dialog("close"); //close the dialog } }, close: function(event, ui) { disableCustomization(); } //every time the dialog is closed.. disableCustomization is launched! }); jQuery('.placeholder').hide(); //hides the placeholders jQuery('.customizer').find('.portlet-header').append('<span class="ui-icon ui-icon-wrench"></span>') //adds the customization button on customizer boxes.. jQuery('.customizer').find('.portlet-header').find('.ui-icon-wrench').click(function() { //and add his action enableCustomization(); }); jQuery('.sortable').sortable({ //instiate columns as sortable to fetch order from cookie handle: '.ui-icon-arrow-4', tolerance: 'pointer', items: '.movable', connectWith: '.sortable', }); getCookie(); //read the cookie jQuery('.sortable').sortable( 'refresh' ) //refresh columns status jQuery('.sortable').sortable('destroy'); //lock columns });
well, i commented every function, i hope you will understand. if not, go to jQuery documentation or view the linked tutorials on top of this post.
however, inside the cookie the order is save with strings like this: b1,b2;b3,b4;b5,b6 where the ; stands for “end of column” so it is most important to put ids in the correct order inside getCookie e setCookie functions. the blocks’ state is saved like this: b1=1,b2=2,b4=1 where 1 stands for minimized and 2 for closed. no need to save “normal state” because we already have it by default (everytime you load the page, every is on default order/state). that’s all. hope you liked! feel free to ask questions!
Hey there, I’m trying to make a custom version of this whereby the portlets “flow” in a certain order when one is moved or removed. Basically, I’d like portlets to only go to one of six locations on the page (as opposed to “stacking” on top of each other if I’ve added them all to one column).
For example, if I move portlet 1 (P1 to P6), then the view should change from:
P1 P2 P3
P4 P5 P6
to
P2 P3 P4
P5 P6 P1
not
P4 P2 P3
P5 P5
P1
Any thoughts on how to do this?
well, i didn’t fully understand your question but i think that you need a different structure from mine.
i designed 3 different column and made blocks moveable between them.
from what you wrote you need a continuous row represented as two rows so you can move a block from head to tail and accordingly move the other to the head of one position.
this “effect” can be done using an unordered list as block container (list =>
). you have to put blocks inside this list, each one inside their- then make the
sortable and put the movement controls on each- .
here it is a link, directly from jquery ui documentation, on how to make sortable an
http://jqueryui.com/demos/sortable/
and here it is a link on taming lists http://www.alistapart.com/articles/taminglists/
see you 🙂