Программируем перетаскиване(Drag and Drop) на JavaScript

Иногда разработчик сталкивается с необходимостью разрешить пользователю свободно перемещать некоторые объекты на странице. Например, такая ситуация возникает при программировании некоторых JavaScript-игр. В этой статье изложена общая теория программирования драга на JavaScript.

Сделать это можно, задав обработчики событий: ondragstart, ondrag, ondragend - для перемещаемого объекта. Но drag-события поддерживаются не всеми версиями современных браузеров. Поэтому мы реализуем универсальную систему, используя обработчики событий onmousedown, onmousemove и onmouseup. Обратите внимание, что при использовании этих событий можно установить любую форму курсора.

Кроме этого мы отключим стандартный обработчик события oncontextmenu, которое возникает при нажатии правой кнопки мыши и приводит к выводу на экран контекстного меню.

Нам нужно установить параметр position в значение absolute и задать начальные координаты.

Перемещаемый объект Перемещаемый объект Перемещаемый объект
Исходный код этого примера:
<img
 src="../plane1b.gif"
 alt='Перемещаемый объект'
 style="position:absolute;left:100px;top:100px;cursor:move;"
 onMousedown="DD.start(event)"
 oncontextmenu="return false"
 ondragstart="return false"
 ondragend="return DD.stop()"
>

<img class='drag'
 src="../plane2b.gif"
 alt='Перемещаемый объект'
 style="position:absolute;left:220px;top:100px;cursor:move;"
>
<img class='drag'
 src="../plane2b.gif"
 alt='Перемещаемый объект'
 style="position:absolute;left:280px;top:150px;cursor:move;"
>

<script type="text/javascript">
var DD = {
    start: function(e) {
	if(typeof DDmove == 'boolean' && DDmove)return false;
	DDobj=getEventTarget(e);
	//if(!DDobj.className=='DRAG')return true;
	DDx=parseInt(DDobj.style.left) - (document.all? event.clientX : e.clientX)
	DDy=parseInt(DDobj.style.top) - (document.all? event.clientY : e.clientY)
	DDmove=true
	//document.onmousemove=DD.drag_drop
	addEvent(document, 'mousemove', DD.drag_drop)
	addEvent(DDobj, 'mouseup', DD.stop)
	//addEvent(DDobj, 'dragstart', DD.f)
	//addEvent(DDobj, 'dragend', DD.stop)
	//addEvent(DDobj, 'dragend', DD.stop)
	DDobj.style.zIndex=100; // сделать верхним
	//addEvent(DDobj, 'selectstart', DD.f, true);
},
    stop: function(e) {
        DDmove=false
	//document.onmousemove=null
	removeEvent(document, 'mousemove', DD.drag_drop)
	removeEvent(DDobj, 'mouseup', DD.stop)
	DDobj.style.zIndex=0;
	return true
},
    drag_drop: function(e) {
	if (document.all&&DDmove){
	   DDobj.style.left=DDx+event.clientX
	   DDobj.style.top =DDy+event.clientY
	   return false
	}
	else if (document.getElementById&&!document.all&&DDmove){
	   DDobj.style.left=DDx+e.clientX+"px"
	   DDobj.style.top =DDy+e.clientY+"px"
	   return false
	}
},
    init: function(c) {
	el=getElementsByClass('drag');
	for ( i=0;i < el.length;i++ ) {
	   DDobj=el[i];
	   addEvent(DDobj, 'mousedown', DD.start);
	   addEvent(DDobj, 'contextmenu', DD.f, true);
	   DDobj.draggable=false;
	}
},
	f:function(e){
	e = e || window.event
	if(e && e.stopPropagation) e.stopPropagation();       // для DOM-совместимых браузеров
	else window.event.cancelBubble = true; //для IE
	return false},

	t:function(e){return true}
}
DD.init();
</script>
    Класс "Drag and Drop" имеет следущие методы:
  • DD.start - нажата кнопка мыши на перемешаемом объекте. Выполняется для объекта, для которого начато перемещение
  • DD.stop - кнопка мыши отжата, перемешение окончено
  • DD.init - необязательный метод, используется для назначения обработки перемешения всем объектам класса 'drag'

Использованны общие универсальные функции:

// получить объект по его ID
function getObj(objID)
{
    if (document.getElementById) {return document.getElementById(objID);}
    else if (document.all) {return document.all[objID];}
    else if (document.layers) {return document.layers[objID];}
}

// получить объект, в котором возникло событие
function getEventTarget(e) {
  var e = e || window.event;
  var target=e.target || e.srcElement;
  if(typeof target == "undefined")return e; // передали this, а не event
  if (target.nodeType==3) target=target.parentNode;// боремся с Safari
  return target;
}

// назначить обработчик события
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
        elm.addEventListener(evType, fn, useCapture);
    return true;}
else if (elm.attachEvent) {
        var r = elm.attachEvent('on' + evType, fn);
        return r;}
else elm['on' + evType] = fn;
}

// удалить обработчик события
function removeEvent(elem, eventType, handler)
{
  return (elem.detachEvent ? elem.detachEvent("on" + eventType, handler) :
   ((elem.removeEventListener) ? elem.removeEventListener(eventType, handler, false) : null));
}
function getElementsByClass(searchClass,node,tag) {
        var classElements = new Array();
        if ( node == null )
                node = document;
        if ( tag == null )
               tag = '*';
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;

        var pattern = new RegExp(\"(^|\\s)\"+searchClass+\"(\\s|$)\");
        for (i = 0, j = 0; i < elsLen; i++) {
               if ( pattern.test(els[i].className) ) {
                    classElements[j] = els[i];
                   j++;
              }
        }
        return classElements;
}

.