Поведение HTML5 drag & drop

Я широко использую drag-drop для HTML5, и он почти полностью ведет себя с одним небольшим исключением.

Я пытаюсь выделить мои dropzones, когда что-то перетаскивается через страницу. Сначала я попытался выполнить это, поместив слушателей jQuery в тело документа, например:

$("body").live('dragover',function(event){lightdz(event)});
$("body").live('dragexit dragleave drop',function(event){dimdz(event)});

с параметрами lightdz() и dimdz(), изменяющими свойство стиля фонового цвета для всех dropzones на странице, чтобы сделать их выделенными. Это не сработало. Всякий раз, когда перетаскиваемый объект вводил дочерний элемент на странице (например, контейнер div), слушатель помещал бы это как событие dragleave и уменьшал бы dropzone.

Я обошел это, применив слушателя ко всем видимым элементам на странице, а не только к телу. Иногда случалось небольшое видимое мерцание на капельках, когда оно пересекало границу между одним элементом и другим, но выглядело хорошо.

В любом случае, теперь я изменил lightdz() и dimdz(), чтобы они применили анимацию jQuery fadeTo() для всех не-dropzones. Это выглядит потрясающе, когда оно работает, и делает его очень очевидным для пользователя, что он может и не может забыть. Проблема в том, что когда он проходит между границами элементов, он применяет анимацию затухания. Это намного более очевидно, чем случайное мерцание фонового цвета, тем более, что, если объект перемещается по нескольким границам очень быстро, он будет ставить в очередь анимации и повторно закрашивать страницу.

Даже если я не беспокоюсь о анимации fadeTo() и просто изменяю непрозрачность, она намного более заметна, чем фликер фона, поскольку вся страница изменяется, а не только элементы dropzone.

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

Ответы

Ответ 1

Я искренне смущен тем, насколько легко это было.

$("*:visible").live('dragenter dragover',function(event){lightdz(event)});

$("#page").live('dragleave dragexit',function(event)
{
    if(event.pageX == "0")
       dimdz(event);
});

$("*:visible").live('drop',function(event){dimdz(event)});

#page - это контейнер для всей страницы. Если событие dragleave перетаскивает объект за пределы окна браузера, event.pageX будет иметь значение 0. Если это происходит где-то еще, оно будет иметь ненулевое значение.

Ответ 2

Я могу быть слишком сложным здесь, но я бы сделал что-то вроде этого:

var draggingFile = false;
var event2;

//elements with the class hotspots are OK
var hotspots = $(".hotspots");

//Handlers on the body for drag start & stop
$("body").live("dragover", function(event){ draggingFile = true; event2 = event; });
$("body").live("dragexit dragleave drop", function(event){ draggingFile = false; event2 = event; });

//Function checks to see if file is being dragged over an OK hotspot regardless of other elements infront
var isTargetOK = function(x, y){
    hotspots.each(function(i, el){
        el2 = $(el);
        var pos = el2.offset();
        if(x => pos.left && x <= pos.left+el2.width() && y => pos.top && y <= post.top+el2.height()){
            return true;
        }
    });
    return false;
};

//Mousemove handler on body
$("body").mousemove(function(e){
    //if user is dragging a file
    if(draggingFile){
        //Check to see if this is an OK element with mouse X & Y
        if(isOKTarget(e.pageX, e.pageY)){
            //Light em' up!
            lightdz(event2);
        } else { /* Fade em' :( */ dimdz(event2); }
    } else {
        dimdz(); //Having no parematers means just makes sure hotspots are off
    }
});

BTW, который, вероятно, не будет работать сразу с места в карьер, поэтому вам придется немного настроить его для работы с вашим кодом.

Ответ 3

Я попробовал принятое решение здесь, но в итоге использовал setTimeout для решения проблемы. У меня возникла проблема с контейнером на странице, блокирующим элемент drop, если он был размещен сверху, и все еще вызывает проблему, если это был элемент drop.

<body style="border: 1px solid black;">
    <div id="d0" style="border: 1px solid black;">&nbsp;</div>
    <div id="d1" style="border: 1px solid black; display: none; background-color: red;">-&gt; drop here &lt;-</div>
    <div id="d2" style="border: 1px solid black;">&nbsp;</div>
    <div style="float: left;">other element</div>
    <div style="float: left;">&nbsp;-&nbsp;</div>
    <div style="float: left;">another element</div>
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
</body>
<script type="text/javascript">
    var resetTimer;

    var f = function(e)
    {
        if (e.type == "dragover")
        {
            e.stopPropagation();
            e.preventDefault();
            if (resetTimer)
            {
                clearTimeout(resetTimer);
            }
            document.getElementById('d1').style.display = '';
        }
        else
        {
            var f = function()
            {
                document.getElementById('d1').style.display = 'none';
            };
            resetTimer = window.setTimeout(f, 25);  
        }
    };

    document.body.addEventListener("dragover", f, true);
    document.body.addEventListener("dragleave", f, true);
    document.getElementById('d1').addEventListener("drop", function(e){ f(); alert('dropped'); }, false);
</script>

Если вам нужно просто вызвать f(); вместо window.setTimeout(f, 250);, вы увидите некоторое мерзкое мерцание элемента, показывающего и скрывающегося.

http://jsfiddle.net/guYWx/