Modifying webkitdragdrop.js uses classes instead of ID

I found a script that seems perfect for my needs, but it uses identifiers, not classes, to create ipad-friendly drag and drop elements.

I really need to use classes, since draggable items could potentially be in the thousands.

[edit] I'm not that good at javascript, and I'm having difficulty understanding how I can change the script to use classes instead of identifiers.

I also contacted the author of the script, but did not receive a response from him.

I offer this award because I did not have a response to my initial request.

Can someone modify the script below to use classes? [/ Edit]

Below is the script as a whole, and here is the script page (the API did not help me in using the vs id classes).

// webkitdragdrop.js v1.0, Mon May 15 2010
//
// Copyright (c) 2010 Tommaso Buvoli (http://www.tommasobuvoli.com)
// No Extra Libraries are required, simply download this file, add it to your pages!
//
// To See this library in action, grab an ipad and head over to http://www.gotproject.com
// webkitdragdrop is freely distributable under the terms of an MIT-style license.


//Description
// Because this library was designed to run without requiring any other libraries, several basic helper functions were implemented
// 6 helper functons in this webkit_tools class have been taked directly from Prototype 1.6.1 (http://prototypejs.org/) (c) 2005-2009 Sam Stephenson

var webkit_tools = 
{
    //$ function - simply a more robust getElementById

    $:function(e)
    {
        if(typeof(e) == 'string')
        {
            return document.getElementById(e);
        }
        return e;
    },

    //extend function - copies the values of b into a (Shallow copy)

    extend:function(a,b)
    {
        for (var key in b)
        {
            a[key] = b[key];    
        }   
        return a;
    },

    //empty function - used as defaut for events

    empty:function()
    {

    },

    //remove null values from an array

    compact:function(a)
    {
        var b = []
        var l = a.length;
        for(var i = 0; i < l; i ++)
        {
            if(a[i] !== null)
            {
                b.push(a[i]);
            }
        }
        return b;
    },

    //DESCRIPTION
    //  This function was taken from the internet (http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/) and returns 
    //  the computed style of an element independantly from the browser
    //INPUT
    //  oELM (DOM ELEMENT) element whose style should be extracted
    //  strCssRule element

    getCalculatedStyle:function(oElm, strCssRule)
    {
        var strValue = "";
        if(document.defaultView && document.defaultView.getComputedStyle){
            strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
        }
        else if(oElm.currentStyle){
            strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
                return p1.toUpperCase();
            });
            strValue = oElm.currentStyle[strCssRule];
        }
        return strValue;
    },

    //bindAsEventListener function - used to bind events

    bindAsEventListener:function(f,object) 
    {
        var __method = f;
        return function(event) {
            __method.call(object, event || window.event);
        };
    },

    //cumulative offset - courtesy of Prototype (http://www.prototypejs.org)

    cumulativeOffset:function(element) 
    {
        var valueT = 0, valueL = 0;
        do {
          valueT += element.offsetTop  || 0;
          valueL += element.offsetLeft || 0;
          if (element.offsetParent == document.body)
            if (element.style.position == 'absolute') break;

          element = element.offsetParent;
        } while (element);

        return {left : valueL, top : valueT};
    },

    //getDimensions - courtesy of Prototype (http://www.prototypejs.org)

    getDimensions: function(element) 
    {
        var display = element.style.display;
        if (display != 'none' && display != null) // Safari bug
          return {width: element.offsetWidth, height: element.offsetHeight};

        var els = element.style;
        var originalVisibility = els.visibility;
        var originalPosition = els.position;
        var originalDisplay = els.display;
        els.visibility = 'hidden';
        if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
          els.position = 'absolute';
        els.display = 'block';
        var originalWidth = element.clientWidth;
        var originalHeight = element.clientHeight;
        els.display = originalDisplay;
        els.position = originalPosition;
        els.visibility = originalVisibility;
        return {width: originalWidth, height: originalHeight};
    },

    //hasClassName - courtesy of Prototype (http://www.prototypejs.org)

    hasClassName: function(element, className) 
    {
        var elementClassName = element.className;
        return (elementClassName.length > 0 && (elementClassName == className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    },

    //addClassName - courtesy of Prototype (http://www.prototypejs.org)

    addClassName: function(element, className) 
    {
        if (!this.hasClassName(element, className))
            element.className += (element.className ? ' ' : '') + className;
        return element;
    },

    //removeClassName - courtesy of Prototype (http://www.prototypejs.org)

    removeClassName: function(element, className) 
    {
        element.className = this.strip(element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
        return element;
    },

    //strip - courtesy of Prototype (http://www.prototypejs.org)

    strip:function(s)
    {
        return s.replace(/^\s+/, '').replace(/\s+$/, '');
    }

}

//Description
// Droppable fire events when a draggable is dropped on them

var webkit_droppables = function()
{
    this.initialize = function()
    {
        this.droppables = [];
        this.droppableRegions = [];
    }

    this.add = function(root, instance_props)
    {
        root = webkit_tools.$(root);
        var default_props = {accept : [], hoverClass : null, onDrop : webkit_tools.empty, onOver : webkit_tools.empty, onOut : webkit_tools.empty};
        default_props = webkit_tools.extend(default_props, instance_props || {});
        this.droppables.push({r : root, p : default_props});        
    }

    this.remove = function(root)
    {
        root = webkit_tools.$(root);
        var d = this.droppables;
        var i = d.length;
        while(i--)
        {
            if(d[i].r == root)
            {
                d[i] = null;
                this.droppables = webkit_tools.compact(d);
                return true;
            }
        }
        return false;
    }

    //calculate position and size of all droppables

    this.prepare = function()
    {
        var d = this.droppables;
        var i = d.length;
        var dR = [];
        var r = null;

        while(i--)
        {
            r = d[i].r;         
            if(r.style.display != 'none')
            {
                dR.push({i : i, size : webkit_tools.getDimensions(r), offset : webkit_tools.cumulativeOffset(r)})           
            }
        }

        this.droppableRegions = dR;
    }

    this.finalize = function(x,y,r,e)
    {
        var indices = this.isOver(x,y);
        var index = this.maxZIndex(indices);
        var over = this.process(index,r);
        if(over)
        {
            this.drop(index, r,e);
        }
        this.process(-1,r);
        return over;    
    }

    this.check = function(x,y,r)
    {
        var indices = this.isOver(x,y);
        var index = this.maxZIndex(indices);
        return this.process(index,r);       
    }

    this.isOver = function(x, y)
    {
        var dR = this.droppableRegions;
        var i = dR.length;
        var active = [];
        var r = 0;
        var maxX = 0;
        var minX = 0;
        var maxY = 0;
        var minY = 0;

        while(i--)
        {
            r = dR[i];

            minY = r.offset.top;
            maxY = minY + r.size.height;

            if((y > minY) && (y < maxY))
            {
                minX = r.offset.left;
                maxX = minX + r.size.width;

                if((x > minX) && (x < maxX))
                {
                    active.push(r.i);
                }           
            }       
        }

        return active;  
    }

    this.maxZIndex = function(indices)
    {
        var d = this.droppables;
        var l = indices.length;
        var index = -1;

        var maxZ = -100000000;
        var curZ = 0;

        while(l--)
        {
            curZ = parseInt(d[indices[l]].r.style.zIndex || 0);
            if(curZ > maxZ)
            {
                maxZ = curZ;
                index = indices[l];     
            }   
        }

        return index;   
    }

    this.process = function(index, draggableRoot)
    {
        //only perform update if a change has occured
        if(this.lastIndex != index)
        {
            //remove previous
            if(this.lastIndex != null)
            {
                var d = this.droppables[this.lastIndex]
                var p = d.p;
                var r = d.r;

                if(p.hoverClass)
                {
                    webkit_tools.removeClassName(r,p.hoverClass);
                }
                p.onOut();
                this.lastIndex = null;
                this.lastOutput = false;
            }

            //add new
            if(index != -1)
            {
                var d = this.droppables[index]
                var p = d.p;
                var r = d.r;

                if(this.hasClassNames(draggableRoot, p.accept))
                {
                    if(p.hoverClass)
                    {
                        webkit_tools.addClassName(r,p.hoverClass);
                    }
                    p.onOver();             
                    this.lastIndex = index;
                    this.lastOutput = true; 
                }
            }   
        }
        return this.lastOutput;
    }

    this.drop = function(index, r, e)
    {
        if(index != -1)
        {
            this.droppables[index].p.onDrop(r,e);
        }
    }

    this.hasClassNames = function(r, names)
    {
        var l = names.length;
        if(l == 0){return true}
        while(l--)
        {
            if(webkit_tools.hasClassName(r,names[l]))
            {
                return true;
            }
        }
        return false;
    }

    this.initialize();
}

webkit_drop = new webkit_droppables();

//Description
//webkit draggable - allows users to drag elements with their hands

var webkit_draggable = function(r, ip)
{
    this.initialize = function(root, instance_props)
    {
        this.root = webkit_tools.$(root);
        var default_props = {scroll : false, revert : false, handle : this.root, zIndex : 1000, onStart : webkit_tools.empty, onEnd : webkit_tools.empty};      

        this.p = webkit_tools.extend(default_props, instance_props || {});
        default_props.handle = webkit_tools.$(default_props.handle);
        this.prepare();
        this.bindEvents();
    }

    this.prepare = function()
    {
        var rs = this.root.style;

        //set position
        if(webkit_tools.getCalculatedStyle(this.root,'position') != 'absolute')
        {
            rs.position = 'relative';
        }

        //set top, right, bottom, left
        rs.top = rs.top || '0px';
        rs.left = rs.left || '0px';
        rs.right = "";
        rs.bottom = "";     

        //set zindex;
        rs.zIndex = rs.zIndex || '0';
    }

    this.bindEvents = function()
    {
        var handle = this.p.handle;

        this.ts = webkit_tools.bindAsEventListener(this.touchStart, this);
        this.tm = webkit_tools.bindAsEventListener(this.touchMove, this);
        this.te = webkit_tools.bindAsEventListener(this.touchEnd, this);        

        handle.addEventListener("touchstart", this.ts, false);
        handle.addEventListener("touchmove", this.tm, false);
        handle.addEventListener("touchend", this.te, false);
    }   

    this.destroy = function()
    {
        var handle = this.p.handle;

        handle.removeEventListener("touchstart", this.ts);
        handle.removeEventListener("touchmove", this.tm);
        handle.removeEventListener("touchend", this.te);    
    }

    this.set = function(key, value)
    {
        this.p[key] = value;
    }

    this.touchStart = function(event)
    {
        //prepare needed variables
        var p = this.p;
        var r = this.root;
        var rs = r.style;
        var t = event.targetTouches[0];     

        //get position of touch
        touchX = t.pageX;
        touchY = t.pageY;

        //set base values for position of root
        rs.top = this.root.style.top || '0px';
        rs.left = this.root.style.left || '0px';
        rs.bottom = null;
        rs.right = null;

        var rootP = webkit_tools.cumulativeOffset(r);
        var cp = this.getPosition();

        //save event properties
        p.rx = cp.x;
        p.ry = cp.y;        
        p.tx = touchX;
        p.ty = touchY;
        p.z = parseInt(this.root.style.zIndex);

        //boost zIndex
        rs.zIndex = p.zIndex;
        webkit_drop.prepare();
        p.onStart();
    }

    this.touchMove = function(event)
    {
        event.preventDefault();
        event.stopPropagation();

        //prepare needed variables
        var p = this.p;
        var r = this.root;
        var rs = r.style;
        var t = event.targetTouches[0];
        if(t == null){return}

        var curX = t.pageX;
        var curY = t.pageY;

        var delX = curX - p.tx;
        var delY = curY - p.ty;

        rs.left = p.rx + delX + 'px';
        rs.top  = p.ry + delY + 'px';

        //scroll window
        if(p.scroll)
        {
            s = this.getScroll(curX, curY);
            if((s[0] != 0) || (s[1] != 0))
            {
                window.scrollTo(window.scrollX + s[0], window.scrollY + s[1]);
            }
        }

        //check droppables
        webkit_drop.check(curX, curY, r);

        //save position for touchEnd
        this.lastCurX = curX;
        this.lastCurY = curY;
    }

    this.touchEnd = function(event)
    {
        var r = this.root;
        var p = this.p;
        var dropped = webkit_drop.finalize(this.lastCurX, this.lastCurY, r, event);

        if(((p.revert) && (!dropped)) || (p.revert === 'always'))
        {
            //revert root
            var rs = r.style;
            rs.top = (p.ry + 'px');
            rs.left = (p.rx + 'px');
        }

        r.style.zIndex = this.p.z;
        this.p.onEnd();
    }

    this.getPosition = function()
    {
        var rs = this.root.style;
        return {x : parseInt(rs.left || 0), y : parseInt(rs.top  || 0)}
    }

    this.getScroll = function(pX, pY)
    {
        //read window variables
        var sX = window.scrollX;
        var sY = window.scrollY;

        var wX = window.innerWidth;
        var wY = window.innerHeight;

        //set contants      
        var scroll_amount = 10; //how many pixels to scroll
        var scroll_sensitivity = 100; //how many pixels from border to start scrolling from.

        var delX = 0;
        var delY = 0;       

        //process vertical y scroll
        if(pY - sY < scroll_sensitivity)
        {
            delY = -scroll_amount;
        }
        else
        if((sY + wY) - pY < scroll_sensitivity)
        {
            delY = scroll_amount;
        }

        //process horizontal x scroll
        if(pX - sX < scroll_sensitivity)
        {
            delX = -scroll_amount;
        }
        else
        if((sX + wX) - pX < scroll_sensitivity)
        {
            delX = scroll_amount;
        }

        return [delX, delY]
    }

    //contructor
    this.initialize(r, ip);
}

//Description
//webkit_click class. manages click events for draggables

var webkit_click = function(r, ip)
{
    this.initialize = function(root, instance_props)
    {
        var default_props = {onClick : webkit_tools.empty};

        this.root = webkit_tools.$(root);
        this.p = webkit_tools.extend(default_props, instance_props || {});
        this.bindEvents();
    }

    this.bindEvents = function()
    {
        var root = this.root;

        //bind events to local scope
        this.ts = webkit_tools.bindAsEventListener(this.touchStart,this);
        this.tm = webkit_tools.bindAsEventListener(this.touchMove,this);
        this.te = webkit_tools.bindAsEventListener(this.touchEnd,this);

        //add Listeners
        root.addEventListener("touchstart", this.ts, false);
        root.addEventListener("touchmove", this.tm, false);
        root.addEventListener("touchend", this.te, false);

        this.bound = true;  
    }   

    this.touchStart = function()
    {
        this.moved = false;
        if(this.bound == false)
        {
            this.root.addEventListener("touchmove", this.tm, false);
            this.bound = true;
        }
    }

    this.touchMove = function()
    {
        this.moved = true;
        this.root.removeEventListener("touchmove", this.tm);
        this.bound = false;
    }

    this.touchEnd = function()
    {
        if(this.moved == false)
        {
            this.p.onClick();
        }
    }

    this.setEvent = function(f)
    {
        if(typeof(f) == 'function')
        {
            this.p.onClick = f;
        }
    }

    this.unbind = function()
    {
        var root = this.root;
        root.removeEventListener("touchstart", this.ts);
        root.removeEventListener("touchmove", this.tm);
        root.removeEventListener("touchend", this.te);
    }

    //call constructor
    this.initialize(r, ip);
}
+5
source share
1 answer

If your class names are unique, the solution is pretty simple. You can change the function $to get by class name instead of id:

var webkit_tools = 
{
    //$ function - simply a more robust getElementById

    $:function(e)
    {
        if(typeof(e) == 'string')
        {
            return document.getElementsByClassName(e)[0];
            // return document.getElementById(e);
        }
        return e;
    },
    ... snipped ...

I confirmed that the above solution works (drag and drop) on my iPhone, but then again, if the class names are not unique, some additional work will be performed in accordance with the current implementation of the script.

~~~ ~~~ EDIT

, , - "" . / , . :

https://gist.github.com/2474416

API. API- dropabble , / , . API / . , draggable/clickable, ref, id classname.

, bulk_draggable(clazzname, options) bulk_clickable(clazzname, options), . draggables/clickables ( ).

, "" API . , script.

+3

All Articles