修改webkitdragdrop.js使用类而不是ID

时间:2012-04-19 06:19:31

标签: javascript ipad drag-and-drop

我找到了一个看起来非常适合我的需求的脚本,但是它使用ID,而不是类来创建ipad友好的拖拽和放大器。放下元素。

我真的需要它来使用类,因为可拖动元素可能有数千个。

[编辑]我在javascript上的表现并不是很好,而且我很难理解如何改变脚本以使用类而不是ID。

我也联系了剧本作者,但他没有回复。

我提供这笔赏金,因为我对原始查询没有任何回应。

请有人可以更改下面的脚本,以便它使用类吗? [/编辑]

下面是完整的脚本,here is the script page(API对我使用class vs id没有帮助)。

// 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);
}

1 个答案:

答案 0 :(得分:3)

如果您的类名是唯一的,那么解决方案就相当简单了。您可以将$函数更改为按类名而不是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 ...

我已经在我的iPhone上验证了上述解决方案的工作原理(拖放),但同样,如果类名不是唯一的,那么根据脚本的当前实现,一些额外的工作将按顺序进行。

~~~ EDIT ~~~

在重新阅读您的请求时,您声明实际上不会有唯一的类名,因此需要某种“批量”拖放功能。我修改/扩展了框架以支持这一点。您可以在此处找到修改版本的来源:

https://gist.github.com/2474416

我不得不稍微更改API。 dropabble API保持不变,因此传递类名只会添加/删除与传递的类名匹配的整个元素列表。可点击/可拖动的API并不那么容易。为了避免严格的重写,我更新了draggable / clickable的初始化方法,以获取元素ref而不是id或classname。

相应地,我添加了bulk_draggable(clazzname, options)bulk_clickable(clazzname, options)函数,它基本上遍历匹配的元素并调用相应的初始值设定项。这些函数返回一个draggables / clickables数组(每个匹配元素一个)。

如果“新”API不清楚,请告诉我。我这样做很快,轻轻地测试了快乐的路径,但是不能投入大量的时间来重写整个脚本。