Пишем скрипт, с помощью которого можно передвигать элементы на web-странице.
Перед тем как взяться за JavaScript проведем простое планирование и ответим на 2 вопроса:
- Что мы будем передвигать?
- Как мы будем передвигать?
Ответ на вопрос «Что?» крайне прост: любой абсолютно (или относительно) позиционированный элемент на странице, например DIV:
На месте DIV-а мог быть рисунок, таблица, ссылка или любой другой элемент.
Ответ на вопрос «Как?» более сложен. Составим простой алгоритм перетаскивания любого элемента:
- Для начала нужно "ухватиться" за элемент, что соответсвует событию mousedown.
- Затем элемент начинают передвигать, что соответствует событию mousemove.
- Когда наш объект оказался на нужном месте его отпускают — событие mouseup.
- Если резко переместить указатель мыши и отпустить вне объекта, то движение тоже должно остановиться — событие mouseup вне элемента.
Для реализации этого алгоритма в жизнь нам понадобятся:
- Глобальные переменные показывающие начальное положение элемента, начальные координаты мыши и состояние движение.
- Функция для определения координат мыши.
- Функция запоминания начального состояния перед движением
- Функция для обработки движения
Соберем всю эту теорию в JavaScript-сценарий:
// Переменная состояния, по умолчанию ничего не двигается = false
var moveState = false;
// Переменные координат мыши в начале перемещения, пока неизвестны
var x0, y0;
// Начальные координаты элемента, пока неизвестны
var divX0, divY0;
// Выведем абсолютно-позиционированный DIV размером 50 * 50
// Зальем DIV черным цветом
// Добавим прямо в DIV обработчики событий
document.write(
"<div \
style='position:absolute; top:0; left:0; background-color:black; width:50px; height:50px;' \
onmousedown = 'initMove(this, event);' \
onmouseup = 'moveState = false;' \
onmousemove = 'moveHandler(this, event);' \
></div>"
);
// Объявим функцию для определения координат мыши
function defPosition(event) {
var x = y = 0;
if (document.attachEvent != null) { // Internet Explorer & Opera
x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
}
if (!document.attachEvent && document.addEventListener) { // Gecko
x = event.clientX + window.scrollX;
y = event.clientY + window.scrollY;
}
return {x:x, y:y};
}
// Функция инициализации движения
// Записываем всё параметры начального состояния
function initMove(div, event) {
var event = event || window.event;
x0 = defPosition(event).x;
y0 = defPosition(event).y;
divX0 = parseInt(div.style.left);
divY0 = parseInt(div.style.top);
moveState = true;
}
// Если клавишу мыши отпустили вне элемента движение должно прекратиться
document.onmouseup = function() {
moveState = false;
}
// И последнее
// Функция обработки движения:
function moveHandler(div, event) {
var event = event || window.event;
if (moveState) {
div.style.left = divX0 + defPosition(event).x - x0;
div.style.top = divY0 + defPosition(event).y - y0;
}
}
Это один из простейший способов реализации Drag&Drop силами JavaScript. Этот сценарий можно улучшить, избавивившись от глобальных переменных, перенеся добавление обработчиков событий из атрибутов тега в JavaScript и многое, многое другое... Главное, чтобы код не потерял свою ясность и работал также быстро. Удачи!

С точки зрения юзабилити, в примере есть один прокол - если быстро двигать мышкой, квадратик отваливается. Курсор вылетает за его пределы и драг заканчивается. Я вылечил это так:
document.onmousemove = function(event) {
moveHandler(document.getElementById('blackbox'), event);
}
(при создании дива придется приписать ему id="blackbox"; не уверен, что это лучшее решение, но советую так или иначе импрувнуть код : ) )
И еще одно пожелание - дайте линк на работающий пример, так читать интереснее : )
P.S. Opera 9.50, в редакторе кнопка «код» не работает.
За баг-репорт спасибо.
использую вот этот скрипт
function GetLayer(layer)
{
var ReturnLayer = null;
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
ReturnLayer = document.getElementById(layer);
else
eval('ReturnLayer = document.' + layer + ';');
}
else
{
eval('ReturnLayer = document.all.' + layer + ';');
}
return ReturnLayer;
}
function Layer(layer, x, y)
{
this.id = GetLayer(layer);
Move(x, y, this.id);
this.ClickedX = 0;
this.ClickedY = 0;
}
function SetZ(z, layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
layer.style.zIndex = z;
if(parseInt(navigator.appVersion) == 4)
layer.zIndex = z;
}
else
{
layer.style.zIndex = z;
}
}
function GetZ(layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
return(parseInt(layer.style.zIndex));
if(parseInt(navigator.appVersion) == 4)
return(layer.zIndex);
}
else
{
return(layer.style.zIndex);
}
}
function GetX(layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
return(parseInt(layer.style.left));
if(parseInt(navigator.appVersion) == 4)
return(layer.left);
}
else
{
return(layer.style.pixelLeft);
}
}
function GetW(layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
return(parseInt(layer.style.width));
if(parseInt(navigator.appVersion) == 4)
return(layer.clip.width);
}
else
{
if(navigator.appVersion.indexOf('MSIE 4') > 0)
return(layer.style.pixelWidth);
else
return(layer.offsetWidth);
}
}
function GetY(layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
return(parseInt(layer.style.top));
if(parseInt(navigator.appVersion) == 4)
return(layer.top);
}
else
{
return(layer.style.pixelTop);
}
}
function GetH(layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
return(parseInt(layer.style.height));
if(parseInt(navigator.appVersion) == 4)
return(layer.clip.height);
}
else
{
if(navigator.appVersion.indexOf('MSIE 4') > 0)
return(layer.style.pixelHeight);
else
return(layer.offsetHeight);
}
}
function Move(x, y, layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
{
layer.style.left = x;
layer.style.top = y;
}
if(parseInt(navigator.appVersion) == 4)
{
layer.left = x;
layer.top = y;
}
}
else
{
layer.style.pixelLeft = x;
layer.style.pixelTop = y;
}
}
function PickUp(layer)
{
var CurrentZ = GetZ(Layers[layer].id);
SetZ(Layers.length - 1, Layers[layer].id);
var currentZ;
for(var index = 0; index < Layers.length; index++)
{
currentZ = GetZ(Layers[index].id);
if(currentZ >= CurrentZ && index != layer)
{
SetZ(currentZ - 1, Layers[index].id);
}
}
}
function Selected(x, y, layer)
{
if(navigator.appName == 'Netscape')
{
if(parseInt(navigator.appVersion) == 5)
if(x > parseInt(layer.style.left) && x < parseInt(layer.style.left) + parseInt(layer.style.width) &&
y > parseInt(layer.style.top) && y < parseInt(layer.style.top) + parseInt(layer.style.height))
return(true);
if(parseInt(navigator.appVersion) == 4)
if(x > layer.left && x < layer.left + layer.clip.width &&
y > layer.top && y < layer.top + layer.clip.height)
return(true);
}
else
{
if(navigator.appVersion.indexOf('MSIE 4') > 0)
{
if(x > layer.style.pixelLeft && x < layer.style.pixelLeft + layer.offsetWidth &&
y > layer.style.pixelTop && y < layer.style.pixelTop + layer.offsetHeight)
return(true);
}
else
{
if(x > layer.style.pixelLeft && x < layer.style.pixelLeft + layer.offsetWidth &&
y > layer.style.pixelTop && y < layer.style.pixelTop + layer.offsetHeight)
return(true);
}
}
return(false);
}
function mouseDown(e)
{
if ((navigator.appName == 'Netscape' && e.which!=1) || (navigator.appName == 'Microsoft Internet Explorer' && event.button!=1)) return true;
var x = (navigator.appName == 'Netscape')? e.pageX : event.x+document.body.scrollLeft;
var y = (navigator.appName == 'Netscape')? e.pageY : event.y+document.body.scrollTop;
if (navigator.appName == 'Netscape' && e.target!=document) routeEvent(e);
ClickedLayer = -1;
//Check if a div was clicked on.
for(var layer = 0; layer < Layers.length; layer++)
{
if(Selected(x, y, Layers[layer].id))
{
if(ClickedLayer == -1)
{
Layers[layer].ClickedX = x - GetX(Layers[layer].id);
Layers[layer].ClickedY = y - GetY(Layers[layer].id);
ClickedLayer = layer;
}
else
//If divs are overlapping, pick the one on top.
if(GetZ(Layers[layer].id) > GetZ(Layers[ClickedLayer].id))
{
Layers[layer].ClickedX = x - GetX(Layers[layer].id);
Layers[layer].ClickedY = y - GetY(Layers[layer].id);
ClickedLayer = layer;
}
}
}
if(ClickedLayer != -1)
{
PickUp(ClickedLayer);
}
if(ClickedLayer == -1) return true;
else return false;
}
function mouseMove(e)
{
var x = (navigator.appName == 'Netscape')? e.pageX : event.x+document.body.scrollLeft;
var y = (navigator.appName == 'Netscape')? e.pageY : event.y+document.body.scrollTop;
if (navigator.appName == 'Netscape' && e.target!=document) routeEvent(e);
//If a div is selected,
//make it follow the mouse cursor.
if(ClickedLayer != -1)
{
Move(x - Layers[ClickedLayer].ClickedX,
y - Layers[ClickedLayer].ClickedY,
Layers[ClickedLayer].id);
}
if(ClickedLayer == -1) return true;
else return false;
}
function mouseUp(e)
{
var x = (navigator.appName == 'Netscape')? e.pageX : event.x+document.body.scrollLeft;
var y = (navigator.appName == 'Netscape')? e.pageY : event.y+document.body.scrollTop;
if (navigator.appName == 'Netscape' && e.target!=document) routeEvent(e);
ClickedLayer = -1;
return true;
}
function init()
{
ClickedLayer = -1;
document.onmousedown = mouseDown;
document.onmousemove = mouseMove;
document.onmouseup = mouseUp;
if(navigator.appName == 'Netscape')
document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP);
Layers = new Array();
Layers[0] = new Layer('Drag', 0, 0);
Layers[1] = new Layer('Drop', 0, 0);
}
</script>
Алгоритм решения задачи прост:
- В процессе мувинга вам известны: ширина блока (divWidth), ширина внешнего контейнера (bodyWidth) и выставляемая left-координата (x).
Далее идут проверки:
else if ( x < bodyWidth - divWidth ) x = bodyWidth - divWidth ;
div.style.left = x + 'px';
Для y аналогично, только с высотами.
P.S. Улучшение, предложенное Frip Nown, можно импрувнуть еще сильнее, если из него сделать функцию такого вида:
{
document.onmousemove = function(event){moveHandler(div, event);}
}
и в div'е заменить
в этом случае не нужно указывать id для div'а и их может быть много
Все разобрался!
Огромное спасибо за помощь! ;)
<img src="1.jpg" style='position:absolute; top:300; left:200'
onmousedown = 'initMove(this, event)'
onmouseup = 'moveState = false'
onmousemove = 'moveHandler(this, event)'>