Okay, so you want to have draggable elements on your page?
Take a look at the following code (and here's a working example). I hope you will find it self-explanatory, but just in case there are also comments:
// Wrap the module in a self-executing anonymous function
// to avoid leaking variables into global scope:
(function (document) {
// Enable ECMAScript 5 strict mode within this function:
'use strict';
// Obtain a node list of all elements that have class="draggable":
var draggable = document.getElementsByClassName('draggable'),
draggableCount = draggable.length, // cache the length
i; // iterator placeholder
// This function initializes the drag of an element where an
// event ("mousedown") has occurred:
function startDrag(evt) {
// The element's position is based on its top left corner,
// but the mouse coordinates are inside of it, so we need
// to calculate the positioning difference:
var diffX = evt.clientX - this.offsetLeft,
diffY = evt.clientY - this.offsetTop,
that = this; // "this" refers to the current element,
// let's keep it in cache for later use.
// moveAlong places the current element (referenced by "that")
// according to the current cursor position:
function moveAlong(evt) {
that.style.left = (evt.clientX - diffX) + 'px';
that.style.top = (evt.clientY - diffY) + 'px';
}
// stopDrag removes event listeners from the element,
// thus stopping the drag:
function stopDrag() {
document.removeEventListener('mousemove', moveAlong);
document.removeEventListener('mouseup', stopDrag);
}
document.addEventListener('mouseup', stopDrag);
document.addEventListener('mousemove', moveAlong);
}
// Now that all the variables and functions are created,
// we can go on and make the elements draggable by assigning
// a "startDrag" function to a "mousedown" event that occurs
// on those elements:
for (i = 0; i < draggableCount; i += 1) {
draggable[i].addEventListener('mousedown', startDrag);
}
}(document));
Load or wrap it in <script></script>
tags as close as possible to </body>
so that it doesn't block the browser from fetching other resources.
Actually, if you remove the comments, it's a very small function. Much smaller and more efficient than the one from the website you've provided.
A possible improvement
Consider replacing the anonymous wrapper with something like makeDraggable(selector);
where selector
is a CSS selector, so you could do crazy stuff like:
makeDraggable('#dragMe, #dragMeToo, .draggable, li:nth-child(2n+1)');
It can be achieved by using document.querySelectorAll
that is able to perform complex CSS queries instead of a simple class name lookup by document.getElementsByClassName
.
Things to watch out for
- If the page has any scrolling - the drag will look broken; consider adjusting the positioning of the dragged element by scrollX and scrollY
- This will obviously not work in Internet Explorer (figure it out yourself).
- There might be memory leaks (needs profiling and testing).
EDIT: A solution for adding new draggable elements
So you want to be able to add more draggable elements? There are several approaches to tackle this. For example you could write a makeDraggable(element);
function and call it on the element you're adding to the DOM. It will work of course, but let's have a look at something different, shall we?
Instead of querying the DOM in search of draggable elements and assigning them event listeners, why don't we assign just one for the "mousedown" event on document body.
When triggered, the event object will contain a reference to the target element which is the object the event has been dispatched on (the element you mousedown-ed). The relevant part of the code will now resemble this:
// Performs a check if the current element is draggable and if yes,
// then the dragging is initiated:
function startDragIfDraggable(evt) {
// Check if the target element (referenced by evt.target) contains a
// class named "draggable" (http://stackoverflow.com/questions/5898656/):
if (evt.target.classList.contains('draggable')) {
// Invoke startDrag by passing it the target element as "this":
startDrag.call(evt.target, evt);
}
}
// Listen for any "mousedown" event on the document.body and attempt dragging
// the target element (the one where "mousedown" occurred) if it's draggable:
document.body.addEventListener('mousedown', startDragIfDraggable);
And here's a working example of the above code. As a bonus it features a simulation of adding new draggable elements to the DOM.
In addition to being able to drag dynamically-added draggable elements, this approach will also save us some memory because we can now avoid assigning a bunch event listeners to a bunch of elements. However, if the application you're developing is very click-intensive (e.g. a game) then you might waste a little bit of CPU because of checks on every click.