Archive for the ‘Javascript’ tag
Drag & drop Uploads with XMLHttpRequest2 and PHP
I finally had some time to read through my ever growing list must read items and play with some new software. While reading up on the new Firefox 3.6 i noticed it came with the new XMLHttpRequest [2] object based on the new file API. And according to the new specs. This would allow for easy file uploads. Now there’s been some examples [2] on the web already. But i just wanted to get my hands dirty.
The new XMLHttpRequest object makes is possible to send files in a few different formats. The most important being the binary format. The code for sending a request with XMLHttpRequest2 looks the same as the previous version. Except for sendAsBinary() in this case.
var xhr = new XMLHttpRequest();
fileUpload = xhr.upload,
fileUpload.onload = function() {
console.log("Sent!");
}
xhr.open("POST", "upload.php", true);
xhr.sendAsBinary(file.getAsBinary());
So let’s set things up for drag & drop. We need a div that will be the main drop point. And we need some event listeners to catch the drag * drop events. Let start by creating the drop zone. For this we use two simple divs. The outer div will listen for the drag & drop events. And the inner will catch the files.
Now let’s create our upload code.
var upload = {
setup : function() {},
uploadFiles : function() {event}
}
window.addEventListener("load", upload.setup, false);
The setup method will set all event listeners for drag & drop. And register the upload handler.
var container = document.getElementById('container');
var drop = document.getElementById('drop');
container.addEventListener("dragenter", function(event) {
drop.innerHTML = '';
event.stopPropagation();
event.preventDefault();
},
false
);
container.addEventListener("dragover", function(event) {
event.stopPropagation();
event.preventDefault();
},
false
);
container.addEventListener("drop", upload.uploadFiles, false);
As you could see above. the uploadFiles() method gets a event returned from the drag & drop action. This is where the new file APi comes in play. To get to the file property we access the dataTransfer object.
var files = event.dataTransfer.files;
The actual uploading is easy as cake.
for (var x = 0; x < files.length; x++) {
var file = files.item(x);
var xhr = new XMLHttpRequest();
fileUpload = xhr.upload,
fileUpload.onload = function() {
console.log("Sent!");
}
xhr.open("POST", "upload.php", true);
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", file.fileName);
xhr.setRequestHeader("X-File-Size", file.fileSize);
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.sendAsBinary(file.getAsBinary());
}
That's it for the client side. There is however a small problem on the receiving side. When handling uploaded files in PHP we expect the $_FILES array to be populated. This is not the case when streaming files from the client to the server. To get the needed file information we set some headers on the client side X-File-Name and X-File-Size. And since the $_FILES are is empty. We need an other way to get the file contents. So we will use php://input streams for that.
The contents of upload.php look like this:
require_once('Streamer.php');
$ft = new File_Streamer();
$ft->setDestination('data/');
$ft->receive();
With setDestination() the destination path for the uploaded files is set. And recieve() listens for any incoming files. Most of the magic is done in the recieve() method. So here's the code.
public function receive()
{
if (!$this->isValid()) {
throw new Exception('No file uploaded!');
}
file_put_contents(
$this->_destination . $this->_fileName,
file_get_contents("php://input")
);
return true;
}
I am impressed! This promises a lot of good. And offers some interesting options. Let's hope all browsers implement this gem. I still have one issue though. I can't get this to work in firefox under linux. The drag & drop events do not seem to function properly with files being dragged from the desktop. anybody know why?
If you interested in the complete code. you can find it here
Animated loader on form submit
In one of our sites we load content of a third party travel organization. And some of these calls can take some time. But when the pages are loading nothing really happens to the browser state. This is confusing for the users of the website. So i decided to add some animated preloader images. Just to show the users the site is still doing stuff.
This however confronted me with a small problem. The preloader images are supposed to show up when a form is submitted. And this is exactly what’s causing the problem. In my first attempt i created the image object when the form was submitted. On submit a JavaScript function was called. This function created the image object and displayed it.
function loader() {
var loaderImg = new Image();
loaderImg.src = 'some/image/url/';
}
This however resulted in an image displayed that had no active animation. This happens i think because the image is not fully loaded when the user hits submit on the form. So to fix this i had to load the images before the page was completely rendered.
var loaderImg = new Image();
window.onload = function() {
loaderImg.src = 'some/image/url';
}
Mootools FX.Slide Flicker bug II
Today i got a question from Richard. About the flicker bug and how to implement the fix. The problem that he had with Javascript throwing an illegal character error was because of copy pasting from this blog. I’m not sure why the single quotes get screwed up. But i will have a look at it. I also didn’t state which version of mootools i was using. It was 1.1 at the time. And i was using the full uncompressed version.
But since then version 1.2 of mootools came out. And id didn’t really had time yet to look into it. But this version also has the same annoying bug. So i looked at the code. The FX.Slide class changed a bit. And the core and plugins are split up into separate files now. But the fix remains the same. And since i don’t really want to write a whole article about it right now. I decided to create two sets of archives. One with the fix for version 1.1 and one for version 1.2. The changes i made are explained in the previous article. But for 1.1 they are around line 4453 and 4458. For 1.2 if you only use the FX.Slide plugin it’s around line 17 and 29.
You can download the archives here:
version 1.1
version 1.2
Mootools FX.Slide Flicker bug
When my coworker decided to use mootools for a project we are working on. I decided to give it a try also.
I decided to use the FX.Slide effect on some tab based boxes. This was pretty straight forward. The mootools library is pretty easy in usage. And the documentation is darn good. Bellow i will show how to add the most basic FX.Slide effect. This is directly copied from the mootools manual.
To create the sliding effect all you need is the following lines of javascript.
window.addEvent('domready', function() {
// create an instance of the FX.Slide object
var mySlide = new Fx.Slide('test');
// Attach the click event to the wrapper div
$('toggle').addEvent('click', function(e) {
e = new Event(e);
mySlide.toggle();
e.stop();
});
});
And of course some HTML to represent the sliding elements.
<h3 class="section">Fx.Slide Vertical</h3> <a id="toggle" href="#">toggle</a> <div id="test"> foo </div>
And some CSS to cover it all in a nice sauce.
#test {
background: #222;
color: #fff;
padding: 10px;
margin: 20px;
border: 10px solid pink;
}
So this looks great. It functions well. And the sliding is very smooth. There is one small problem though. When the wrapper contains an element with a style attribute overflow set to “auto”. The sliding will cause a flicker effect on the page. This is probably caused by rescaling the wrapper. Witch in it’s turn rescales the element with attribute overflow set to auto.
I didn’t really try to figure out why this happens. Maybe that’s a later project. But i did come up with a small fix. When a slide object is created. FX.Slide wraps a div around the container that will slide in and out. This wrapper has the attribute overflow set to hidden. I couldn’t figure out why overflow is set to hidden on this wrapper. So i added the ability to set it through the options object. When overflow is set to auto the flicker bug disappears. To add this feature we need to add some small changes to the FX.Slide class.
options: {
mode: 'vertical'
},
First we change the code above to reflect the code below:
options: {
mode: 'vertical',
overflow: 'hidden'
},
Then on the third line of the initialize function we change:
this.wrapper = new Element('div', {'styles': $extend(this.element.getStyles('margin'), {'overflow': "hidden"})}).injectAfter(this.element).adopt(this.element);
To:
this.wrapper = new Element('div', {'styles': $extend(this.element.getStyles('margin'), {'overflow': this.options.overflow})}).injectAfter(this.element).adopt(this.element);
When all this is done and we want to create a FX.Slide object without the flicker bug. We add the options parameter. The constructor will make sure the right value is used.
var mySlide2 = new Fx.Slide('test', {overflow: 'auto'});
$('toggle').addEvent('click', function(e){
e = new Event(e);
mySlide2.toggle();
e.stop();
});
Missing Gem for webdesign
Lately i’ve been working on a project. And one of the requirements is that the user interface works from IE 6 and up. Now i have to admit that it’s been a while since i did CSS. And forgot what a pain it is to get things working cross browser. There is always those little irritating IE 6 bugs that double your working time because you either need to fix a bug. Or because you need to create a different stylesheet for a certain browser.
So when i hit the 3 pixel gap bug in IE 6 again. I wanted to start and create a javascript based fix. But first did a google search. To see of nobody had the same genius idea. And low and behold. Somebody did. I found this great little Gem ie7-js It’s a Javascript library created by Dean Edwards. For a lot of things their already were fixes floating around the web. But this has it all. It fixes the PNG alpha transparency bug in IE 5.5 and 6.0. Makes sure the :hover element is not only usable on <a> elements. And a whole lot more. The new IE8-js version is even more complete. And adds advanced CSS selectors and properties.
Using it is as simple as including this in the header section of your (X)HTML:
<!--[if lt IE 8]> <script src="IE8.js" type="text/javascript"></script> <![endif]-->






