Saturday, September 13, 2008

Javascript drag and drop

As promised, I got the simple drag and drop script further. Lots of the stuff is still the same, but now we actually drag the element. Most of the new code handles getting the mouse position and moving the element according to it. I even made the dragged item transparent, cause I tought it would be useful to see the content that is under it. I also found a bug in the old script. Instead of using window.onmouseup we must use document.onmouseup to make sure the function works in all browsers. Anyways, the simple script was just an example, now this bug is fixed and everything works cross-browser. I think this script is really usefull, so feel free to put it on your site ;) Here is the javascript code:
//capture the mousemove event and call a function that will get the position of the cursor
if (document.layers) 
{ 
    document.captureEvents(Event.MOUSEMOVE);
    document.onmousemove = captureMousePosition;
} 
else if(document.all || document.getElementById) 
{ 
    document.onmousemove = captureMousePosition;
}

//this variables are used to store the mouse position coordinates
xMousePos = 0; 
yMousePos = 0; 

//this function stores the coordinates into the global variables every time the mouse moves over the page
function captureMousePosition(e) 
{
 if(document.layers) 
 {
  xMousePos = e.pageX;
  yMousePos = e.pageY;
 } 
 else if(document.all) 
 {
  xMousePos = window.event.x+document.body.scrollLeft;
  yMousePos = window.event.y+document.body.scrollTop;
 } 
 else if(document.getElementById) 
 {
  xMousePos = e.pageX;
  yMousePos = e.pageY;
 }
 
 //after we have captured the new mouse position move the dragged element (if there is one) to it 
 if(window.dragged_item)
  moveDraggedItem();
}


//this will store the object that is currently being dragged
var dragged_item = false;

//when the user release the mouse anywhere on the page we need to drop the object.
//creating this global onmouseup event always clears the currently dragged element
document.onmouseup = function()
{
 window.dragged_item = false;
}

//get the element that is being dragged
function get(elmnt)
{
 window.dragged_item = elmnt;
 
 //make the dragged object transparent
 setOpacity(elmnt.id, 50);
}

//drop the element in it's new spot.
function drop(elmnt)
{
 //what we do is access the parent node of the dragged element and remove the element from it.
 //this line works for all objects, no matter if they are part of the whole page (window object) or any other element within the page
 window.dragged_item.parentNode.removeChild(window.dragged_item);
 
 //we won't move the element anymore, so - reset it's position styles
 window.dragged_item.style.position = 'relative';
 window.dragged_item.style.left = 0;
 window.dragged_item.style.top = 0;
 
 //then append the dragged item to it's new parent element
 elmnt.appendChild(window.dragged_item);
 
 //we've droped the element - make it fully visible again
 setOpacity(window.dragged_item.id, 100);
}

//this func changes the opacity of the elements. It makes them transparent. Works in all browsers.
//we will use it to make the dragged element transparent until it is droped.
function setOpacity(id, opacity) 
{
    var object = document.getElementById(id).style;
 
    object.opacity = (opacity / 100);
 object.filter = "alpha(opacity="+opacity+")";
    object.MozOpacity = (opacity / 100);
    object.KhtmlOpacity = (opacity / 100);
}

function moveDraggedItem()
{
 //set the position property to absolute, so we can move the element according to the mouse cursor
 window.dragged_item.style.position = 'absolute';

 //now move it to the mouse coordinates. I add 5 pixels to the bottom right, cause otherwise the
 //onmouseup event on the elements that the dragged one is being droped to, will not work..
 window.dragged_item.style.left = xMousePos+5;
 window.dragged_item.style.top = yMousePos+5;
}
The HTML that is used has not changed a lot, except that we now need to put an id to each element. The id is used in setting the opacity of elements. Here's the HTML I used for testing:
<div onmousedown='get(this);' id='1' style='border: 1px solid #333; width: 50px; height: 50px; background: #f99'></div>
<div onmousedown='get(this);' id='2' style='border: 1px solid #333; width: 50px; height: 50px; background: #9f9'></div>
<div onmousedown='get(this);' id='3' style='border: 1px solid #333; width: 50px; height: 50px; background: #99f'></div>

<div onmouseup='drop(this);' id='a' style='border: 1px solid #333; width: 200px; height: 200px;'></div>

<div onmouseup='drop(this);' id='b' style='border: 1px solid #333; width: 200px; height: 200px'></div>
You can see the new drag and drop script here. Just "save page as..." to get the code, for some reason freehostia (or is it just me?) is having some issues with the rar files i used to upload and I don't have the time to fix it now..

Sunday, September 7, 2008

Simplies javascript drag and drop

Drag and drop was something very common for desktop applications and yet rarely found on a web page. Well, a lot of things changed and over the last few years web pages are becoming more and more flexible and functional. Having faster browsers and lots of big, complicated projects (i.e Gmail) is pushing web masters and coders to offer more and more functionality within their pages. In this article I will show you what is the simplies way to implement a drag and drop interface. I call it 'simpliest', cause that's what it is - the very simple, ugly and primitive way to implement drag and drop. Funny thing is, that drag and drop is so commonly mistaken for something so hard to accomplish, that a lot of people are scared to even think about implementing it. Here I'll show you how with as little as 10-15 lines of code you can have drag and drop, working on each and every element on your page.

First, we need to get the dragged element and store it somewhere. This will be triggered by a onmousedown event:
//this will store the object that is currently being dragged
var dragged_item = false;


//get the element that is being dragged
function get(elmnt)
{
        //simply save the element (not it id or something - the whole HTML element) into our global var
 window.dragged_item = elmnt;
}
Now we can simply add a onmousedown='get(this);' to any object that we want to be able to drag and drop. Having the element in window.draged_item we can 'drop' it somewhere else:
//drop the element in it's new spot.
function drop(elmnt)
{
 //what we do is access the parent node of the dragged element and remove the element from it.
 //this line works for all objects, no matter if they are part of the whole page (window object) or any other element within the page
 window.dragged_item.parentNode.removeChild(window.dragged_item);
 
 //then append the dragged item to it's new parent element
 elmnt.appendChild(window.dragged_item);
}
Now all we need to do is add a onmouseup='drop(this);' event trigger to every object on the page where we want to be able to drop stuff into. Voila, actually this simple two functions are enough for a drag and drop functionality. However, I will add something else to make things a bit better:
//when the user release the mouse anywhere on the page we need to drop the object.
//creating this global onmouseup event always clears the currently dragged element
window.onmouseup = function()
{
 window.draged_item = false;
}
Not having this global onmouseup trigger will cause the elements to be dragged forever. The user can actually click the mouse somewhere on a blank space within the page, but he will still be able to drop the element once he clicks on another element, which has a onmouseup='drop(this);' trigger.. Creating the global function handles that and makes sure that when the user has released the mouse button the element will be droped, either onto it's new location, or into it's old.
That's it for now, I'll surely write more about the subject. Soon I will show you how to actually drag the element on the page, not just imagine that it is being dragged :) You can see two demos of the above code. In the first one I use some div elements. It can be seen here.
In the second demo I use some unordered lists. In this demo you can see what I mean by ugly drag and drop - you can drag the list elements from one list to another, but while doing it you select some of the text, some of it is still selected when you drop the element, etc, etc. Next time I will show you how to fix this issues.

Friday, September 5, 2008

Javascript on Google Chrome

This morning I found something very interesting. The last few days, after Google's new open source browser Chrome's beta version has been released, there have been a lot of discussions on how good or bad it is. One of the things that amased me is the fallowing test:


It's clear too see that Chrome is much faster than other browsers in terms of javascript execution. Google were right to tell that this browser will be much more 'usable' for rich web applications, as having fast javascript means that you can build stuff on the web that runs almost as smoothly as desktop applications. This is great news to me, and a lot of other developers, I believe. So, congrats guys, let's now hope that Chrome's issues will soon be fixed and there will not be lots of differences on how it handles the same code, compared to other browsers... :)

P.S You can run the tests from the picture here.

Monday, September 1, 2008

Javascript digital countdown clock

This is the conclusion of the digital clocks scripts. A countdown clock that is made in pretty much the same way as the others, but it countdowns a given time to zero. This has a lot of "real life" applications. IMHO, maybe the most appropriate usage is to tell the user how much session time he has left. How much time he has to spend waiting for that damn download to begin, etc., etc. Lots of the code is about the same as in the clock and the counter. Here it is:
window.onload = function()
{
 //create the clock just after the page has loaded
 draw_clock('countdown', '00:00:04');
}

function draw_clock(id, starting_time)
{
 //create the div that will hold the clock
 var clock = document.createElement('div');
 
 clock.id = id;
 
 //you can change the style of the clock ie to position it at a specific place on the page
 //i'll put this one approximately in the center of the page, but you can adjust that to fit your needs
 clock.style.position = 'absolute';
 clock.style.left = '50%';
 clock.style.top = '50%';
 clock.style.marginLeft = '-50px';
 clock.style.marginTop = '-20px';
 
 //keep the current time in an unexisting property (for the div element) - 'time'. Instead of declaring a global variable
 //you can use the current objects you have. This makes it a lot easier to remember what is what. If you are familiar 
 //with OOP, you can go even furher by declaring new javascript objects. Well, I will digg further into that OOP stuff later,
 //in another script :)
 clock.time = starting_time; 
 
 //get the current time and set it as div's innerHTML, using a function that formats it to look good on the page
 clock.innerHTML = get_clock_innerHTML(clock.time);

 //add the element to the page
 document.body.appendChild(clock);
 
 //set the timer which will update the clock. The interval can also be set to more or less than a second. 
 //If you need to update the time in another interval of time, say 15 seconds, you can use 15000 as the second argument to setInterval()
 window.countdown_interval = setInterval("clock_timer('"+id+"')", 1000);
}

function get_clock_innerHTML(time)
{
 //an array that stores the symbols which draw the good old-fashioned digital clock :) 
 //IMPORTANT: don't change the alignment or anything else on this array, cause this will cause the clock to look uglier, or even not work
 var digits = " _     _  _     _  _ _   _  _    "+
     "| | |  _| _||_||_ |_  | |_||_| . "+
     "|_| | |_  _|  | _||_| | |_| _| . "+
     "                                 ";

 //put the symbols inside a <pre> tag to make sure they will be displayed just like they are written in the array.
 //you can also change the style settings i've set
 var data = "<pre><span style='color: #181; background: #111; font-size: 10px;'>";

 //loop trough the four 'rows' of the array and display the appropriate symbols
 for(r = 0; r < 100; r += 33)
 {
  for(i = 0; i < time.length; i++)
  {
   for(c = (time[i])*3 + r , b = 0; b < 3; b++, c++)
   {
    if(isNaN(c))
     c = 10*3+r;

    data += digits[c];
   }
  }

  data += "<br />";
 }
 
 data += "</span></pre>";

 return data;
}

function clock_timer(id)
{
 var clock = document.getElementById(id);

 //get the current time and split it into hours minutes and seconds
 var tmp = clock.time.split(":");
 
 var hours   = tmp[0];
 var minutes = tmp[1];
 var seconds = tmp[2];
 
 //check what to decrement. If the seconds have reached zero we need to change either the minutes or both - minutes and hours
 if(seconds == 0)
 {
  //if the minutes have reached zero, decrement the hours and reset minutes and seconds.
  if(minutes == 0)
  {
   hours--;
   minutes = 59;
    seconds = 59;
  }
  else //just decrement the minutes and reset seconds
  {
   minutes--;
   seconds = 59;
  }
 }
 else //nothing interesting happend this last second - just decrement seconds and go on :)
  seconds--;
 
 //if the time has elapsed - call the function to destroy the counter and do someting else if you need to
 if(hours == 0 && minutes == 0 && seconds == 0)
  countdown_finished(id);
 
 //Save the new time in the appropriate form of HH:MM:SS
 clock.time = ((hours < 10) ? "0"+parseInt(hours) : hours)+
 ":"+ ((minutes < 10) ? "0"+parseInt(minutes) : minutes) +
 ":"+ ((seconds < 10) ? "0"+parseInt(seconds) : seconds);
 
 //update the clock with the new time
 clock.innerHTML = get_clock_innerHTML(clock.time);
}
That was easy, everything looks just like the counter, except we're counting down this time. Here is a little something that makes a lot of difference - the countdown_finished() function. It is called when the time has elapsed. This is the place where you can stop the countdown, alert() the appropriate message to the user, etc., etc. Do what you have to do when the time has elapsed. Here are some examples of what you might want to do:
  • The simpliest one - stop the countdown and tell the user the time has elapsed:
    function countdown_finished(id)
    {
     //clear the interval, cause the time has elapsed
     clearInterval(window.countdown_interval);
     
     alert('The time has elapsed!');
    }
    
    You can see a demo here and download the code from here.

  • Again, tell the user the countdown has finished, but this time remove the clock from the page:
    function countdown_finished(id)
    {
     //clear the interval, cause the time has elapsed
     clearInterval(window.countdown_interval);
     
     alert('The time has elapsed! It\' time to dissapear..');
     
     //get rid of the clock, we don't need it anymore ]:->
     document.body.removeChild(document.getElementById(id));
    }
    
    You can see a demo here and download the code from here.

  • Tell the user the time has elapsed and send him somewhere else:
    function countdown_finished(id)
    {
     //clear the interval, cause the time has elapsed
     clearInterval(window.countdown_interval);
     
     //tell the user he has had his time on that page..
     alert('Go away! You\'re not welcome anymore!');
     
     //and send him somewhere else
     window.location = "http://javascripthowtos.blogspot.com/";
    }
    
    You can see a demo here and download the code from here.

  • Redirect the user to the download he has waited for:
    function countdown_finished(id)
    {
     //clear the interval, cause the time has elapsed
     clearInterval(window.countdown_interval);
     
     //time has elapsed - redirect the user to the long waited download..
     alert('Ok, now you know how I look and what I can do. Now, download me!');
     
     //set the new location - the file to download
     window.location = "javascript digital countdown clock4.zip";
    }
    
    You can see a demo here and download the code from here.

  • After telling the user the time has elapsed - start the countdown again. This is also very usable if you wan't to do stuff over a period of time, and want to user to know how much time is left till 'stuff' happens.. :)
    function countdown_finished(id)
    {
     //clear the interval, cause the time has elapsed
     clearInterval(window.countdown_interval);
     
     alert('OK, one down! There goes another one!');
     
     //The countdown is dead. Long live the countdown! :)
     //create a new timeout for another countdown
     var new_timeout = Math.ceil(Math.random()*10);
     
     //add a leading zero if the number is just one digit
     if(new_timeout < 10)
      new_timeout = "0"+new_timeout;
     
     //create the new countdown clock, even use the same id as before, but use the new timeout
     draw_clock('countdown', "00:00:"+new_timeout); 
    }
    
    You can see a demo here and download the code from here.
The possibilities are endless - do whatever you need to do when the time has elapsed :)