2

I found this script by theZillion (http://thezillion.wordpress.com/2012/08/29/javascript-draggable-no-jquery/) that makes a div draggable. I'm trying to use this script to move a div by class name. And not by ID.

I have an event handler that works, but not when I'm adding the script... The console shows no errors either. Any ideas about how to make this work?

This is the code I have:

function wrappmover(){
    var moveEvent = "dice-window-wrapper";
    var addClassArr= document.getElementsByClassName(moveEvent);
    for(var i=0; i<addClassArr.length; i++){
        var addClass = addClassArr[i];
        addClass.addEventListener("click", movewrapp, true);
    }       
    function movewrapp() {
        var classToMove = "dice-window-wrapper";
        var elems = document.getElementsByClassName(classToMove);
        var tzdragg = function(){
            return {
                 startMoving : function(evt){ 
                     evt = evt || window.event;
                     var posX = evt.clientX, 
                     posY = evt.clientY, 
                     a = document.getElementsByClassName(classToMove),
                     divTop = a.style.top,
                     divLeft = a.style.left;
                     divTop = divTop.replace('px','');
                     divLeft = divLeft.replace('px','');
                     var diffX = posX - divLeft, 
                     diffY = posY - divTop; 
                     document.onmousemove = function(evt){ 
                         evt = evt || window.event;
                         var posX = evt.clientX,
                         posY = evt.clientY, 
                         aX = posX - diffX,
                         aY = posY - diffY; 
                         tzdragg.move('elem',aX,aY);
                     }
                 },
                 stopMoving : function(){ 
                    document.onmousemove = function(){}
                 },
                 move : function(divid,xpos,ypos){ 
                     var a = document.getElementById(divid);
                     document.getElementById(divid).style.left = xpos + 'px';
                     document.getElementById(divid).style.top = ypos + 'px';
                 }
            }
        }();
Oleg
  • 9,341
  • 2
  • 43
  • 58
Dymond
  • 2,158
  • 7
  • 45
  • 80
  • Respectfully, why not just use jQuery UI? You can make a `div` draggable as simply as `$('div').draggable();`! – user1477388 Feb 04 '13 at 21:38
  • Could you please elaborate on "an event handler that works" (how do you know it works?) and "but not when I'm adding the script" (how, where and when are you adding it?). – Oleg Feb 04 '13 at 21:38
  • @user1477388 , Im using javascript for learning purposes. – Dymond Feb 04 '13 at 21:42
  • @Oleg the script begins with var tzdragg = function(){ How i know that the event handler work. I putted an Alert connected to the class dice-window-wrapper. everytime i click it alert me. – Dymond Feb 04 '13 at 21:43

1 Answers1

3

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.

Oleg
  • 9,341
  • 2
  • 43
  • 58
  • Hey! awesome. I'm trying to reverse engineer your code now, since I'm creating classes dynamically, and now it reads only classes thats already in the wrap. any tips on where it should be ? Anyhow. many thanks. – Dymond Feb 05 '13 at 02:12
  • Hey.@Oleg Solved the problem by adding your code inside my eventhandler! Thanks you again. – Dymond Feb 05 '13 at 07:25
  • Very helpful even a decade later, thank you – Nostrildumbass Apr 06 '23 at 16:34