11

私は現在、ウェブサイト デザイナーを構築しています。コア機能の 1 つは、要素をドラッグ アンド ドロップして再配置できることです。私はこの機能に数日間取り組んできましたが、何度か動かなくなりました。このドラッグ アンド ドロップ システムに関する最も重要な注意点は、ドラッグ可能な要素はマスターコンテナー内の任意の場所にドロップでき、所定の位置にスナップされるため、絶対に配置された要素が存在しないということです。

最初に、要素をドラッグできるコアドラッグ可能なビットを構築することから始めました。次に、要素をドロップするとdocument.elementFromPoint()、カーソル位置にある要素を取得するために使用しています(注:ドラッグ可能な要素を非表示にする必要があります。それを返します)。

これで、カーソルに最も近い要素ができました。主な問題は、4 つのオプションがあるため、ドラッグ可能な要素をその要素に対してどこに移動する必要があるかを把握することappend - prepend - insertBefore - insertAfterです。なんとか機能しましたが、ターゲットの高さとオフセットを使用してどちらかを決定するだけであり、上のパラメーターを増やして、短い距離で別の要素にヒットしているかどうかを確認しappend - prepend & insertBeforeているため、信頼性が十分ではありません。. ここに私がこれまでに持っているコードがあります。append or prependYgetFromPointinsertBefore

box.addEventListener('mouseup', function (e) {
    if (!dragging) return;
    dragging = false;
    var thisEl = this; // the drag-able element

    // Hide 
    this.style.display = 'none'; // hide the element so we can get 
                                 // document.elementFromPoint

    var el = document.elementFromPoint(e.pageX, e.pageY), // target
        elH = el.offsetHeight, // height of target
        elT = el.offsetTop; // offset of target

    for (var i = 0; i < 15; i++) { // This is a weird part see the reference at the bottom
        var newEl = document.elementFromPoint(e.pageX, e.pageY + i);
        if (newEl !== el) {
            this.style.display = 'block';
            this.style.position = 'static';
            return newEl.parentNode.insertBefore(thisEl, newEl);
        }
    }

    if (e.pageY < elT + (elH / 2)) { // if the pageY is less than the target offset + half of the height, that's how I am calculating prepend
        el.appendChild(this);
        el.insertBefore(this, el.firstChild);
    } else {
        el.appendChild(this); // else append;
    }

    this.style.display = 'block';
    this.style.position = 'static'; // Snap back in with 'static'
});

これは、mouseupすべての作業を行う私のイベントです。他のイベントは要素をドラッグ可能にするだけで、それほど重要ではありません。

ここにフィドルがあります

http://jsfiddle.net/mLX5A/2/

それで質問がなかった場合は、ここに短いバージョンがあります。

私のドラッグ可能な要素は、どこにでもドロップしてスナップできる必要があります。私がフィドルで行った方法は間違いなく良くないので、これを行う最善の方法は何ですか。のターゲットに対して要素が移動する必要がある場所を検出するにはどうすればよいですかmouseup

奇妙なセクションへの参照。

これがどのように機能するかです(警告、良くありません)。ターゲット要素を取得すると、 15回ループして値をelementFromPointインクリメントするループを作成するので、基本的には下に移動し、その短いスペース内で新しい要素にヒットした場合、挿入したいと想定していますターゲットの前の要素。YelementFromPointelementFromPoint15px

このコードとは関係のない回答をいただければ、他のユーザーにもメリットがあります。

ドラッグ可能なコンテナーは、デザイナーの主要部分であることに注意してください。したがって、すべての要素を絶対配置することは私にとって選択ではありません。要素をすべての可能な場所に配置して、ドラッグ可能な要素がどこにあるのかを検出できるようにすることは、本当に良い考えではありません。そのコンテナは、不要なコンテンツのない高品質の結果になります。

また、私のアプリケーションは古いブラウザー(IE6、IE7、IE8、IE9) をサポートしておらず今後もサポートしないことに注意してください。

4

6 に答える 6

3

ChaseMoskal は contenteditable="true" を使用して問題を解決しています。

HTML

<!--====  DragonDrop Demo HTML
Here we have two contenteditable <div>'s -- they have a dashed bottom-border dividing them, and they contain various text content. The first <div> contains structured block content, and the second <div> contains Unstructured, unwrapped content.
====-->

<div contenteditable="true">
    <h1>Athenagora Lenoni Incommunicabile: Structured Content</h1>
    <h2>Cellam modico illius ergo accipiet si non ait est Apollonius.</h2>

    <a class="fancy">
        <img src="http://imageshack.us/scaled/landing/809/picture195z.jpg" />
        <caption>Hola!</caption>
    </a>

    <p>Volvitur ingreditur lavare propter increparet videns mihi cum. Tharsis ratio puella est Apollonius in deinde plectrum anni ipsa codicellos, jesus Circumdat flante vestibus lumine restat paralyticus audi anim igitur patriam Dianae. 'Iuraveras magnifice ex quae ad per te sed dominum sit Mariae Bone de his carpens introivit filiam. Plus damna nautis unum ad te. Puto suam ad quia iuvenis omnia. Etiam quantitas devenit regi adhibitis sedens loculum inveni.</p>
</div>

<div contenteditable="true">
    <strong>Unstructured Content:</strong> Toto determinata se est se ad te finis laeta gavisus, laetare quod una non ait mea ego dum est Apollonius. Intrarem puella est in deinde cupis ei Taliarchum in, tharsiam vis interrogat Verena est Apollonius eius ad suis. Antiochus ac esse more filiam sunt forma ait Cumque persequatur sic. Imas rebum accusam in fuerat est se sed esse ait Cumque ego. Secundis sacerdotem habemus ibi alteri ad quia, agere videre Proicite a civitas exulto haec. Supponite facultatibus actum dixit eos. Neminem habere litore in deinde duas formis. Quattuordecim anulum ad nomine Hesterna studiis ascende meae puer ut sua, eiusdem ordo quos annorum afferte Apollonius non ait in.
    <br /><br />
    Deducitur potest contremiscunt eum ego Pentapolim Cyrenaeorum tertia navigavit volente in fuerat eum istam vero cum obiectum invidunt cum. Christe in rei sensibilium iussit sed, scitote si quod ait Cumque persequatur sic. Amet consensit cellula filia in rei civibus laude clamaverunt donavit potest flens non solutionem innocentem si quod ait. Una Christi sanguine concomitatur quia quod non coepit, volvitur ingreditur est Apollonius eius non potentiae. Coepit cenam inhaeret Visceribusque esocem manibus modi regiam iriure dolore. Filiam in rei finibus veteres hoc ambulare manu in fuerat eum istam provoces.
</div>

<button name="toggleContentEditable">disable contenteditable</button>

CSS

/*======   DragonDrop Demo CSS
Basically, user-select, and user-drag (and all their prefixes) need to be set in a way that lets the browser know which parts of our draggable element are selectable and draggable and which aren't.
    So I guess this is that.
                   ======*/
@charset utf-8;

/* these rules only apply to fancy boxes when contenteditable is true: they are necessary for the drag-and-drop demo to work correctly */
[contenteditable="true"] .fancy {
    /**/-moz-user-select:none;-webkit-user-select:none;
    user-select:none; /* without this line, element can be dragged within itself! No-no! */
    /**/-moz-user-drag:element;-webkit-user-drag:element;
    user-drag:element; /* makes the whole fancy box draggable */
    cursor:move !important; } /* switches to the move cursor */
    [contenteditable="true"] .fancy * {
        /**/-moz-user-drag:none;-webkit-user-drag:none;
        user-drag:none; } /* hopefully disables the internal default dragging of the img */



/*======     Everything below this area
               is STRICLY for STYLE
                 and VISUAL APPEAL.
              It shouldn't concern you.    ======*/

html,body{ height:100%; }
[contenteditable] {
    background:#f8f8f8; box-sizing:border-box; padding:2%; min-height:auto;
    font-size:10px; font-family:sans-serif; color:#444;
    border-bottom:2px dashed #888; }
    .fancy {
        display:inline-block; margin:10px;
        color:#444; text-align:center; font-style:italic;
        font-size:10px; font-family:sans-serif;
        background:#fff; border:8px solid white;
        box-shadow:1px 2px 8px #444;
        cursor:pointer; }
        .fancy img {
            display:block; margin-bottom:2px;
            border:1px solid white;
            box-shadow:0 1px 6px #888; }
        .fancy .caption { max-width:100px; }
    h1 { font-weight:bold; font-size:1.4em; }
    h2 { font-weight:bold; font-style:italic; text-indent:2px; }
    p { text-indent:8px; }
    strong { font-weight:bold; }
button { display:block; margin:6px auto; }

Javascript

/*

==== Dragon Drop: a demo of precise DnD
          in, around, and between 
         multiple contenteditable's.

=================================
== MIT Licensed for all to use ==
=================================
Copyright (C) 2013 Chase Moskal

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
============

*/

function DRAGON_DROP (o) {
    var DD=this;

    // "o" params:
    DD.$draggables=null;
    DD.$dropzones=null;
    DD.$noDrags=null; // optional

    DD.dropLoad=null;
    DD.engage=function(o){
        DD.$draggables = $(o.draggables);
        DD.$dropzones = $(o.dropzones);
        DD.$draggables.attr('draggable','true');
        DD.$noDrags = (o.noDrags) ? $(o.noDrags) : $();
        DD.$dropzones.attr('dropzone','copy');
        DD.bindDraggables();
        DD.bindDropzones();
    };
    DD.bindDraggables=function(){
        DD.$draggables = $(DD.$draggables.selector); // reselecting
        DD.$noDrags = $(DD.$noDrags.selector);
        DD.$noDrags.attr('draggable','false');
        DD.$draggables.off('dragstart').on('dragstart',function(event){
            var e=event.originalEvent;
            $(e.target).removeAttr('dragged');
            var dt=e.dataTransfer,
                content=e.target.outerHTML;
            var is_draggable = DD.$draggables.is(e.target);
            if (is_draggable) {
                dt.effectAllowed = 'copy';
                dt.setData('text/plain',' ');
                DD.dropLoad=content;
                $(e.target).attr('dragged','dragged');
            }
        });
    };
    DD.bindDropzones=function(){
        DD.$dropzones = $(DD.$dropzones.selector); // reselecting
        DD.$dropzones.off('dragleave').on('dragleave',function(event){
            var e=event.originalEvent;

            var dt=e.dataTransfer;
            var relatedTarget_is_dropzone = DD.$dropzones.is(e.relatedTarget);
            var relatedTarget_within_dropzone = DD.$dropzones.has(e.relatedTarget).length>0;
            var acceptable = relatedTarget_is_dropzone||relatedTarget_within_dropzone;
            if (!acceptable) {
                dt.dropEffect='none';
                dt.effectAllowed='null';
            }
        });
        DD.$dropzones.off('drop').on('drop',function(event){
            var e=event.originalEvent;

            if (!DD.dropLoad) return false;
            var range=null;
            if (document.caretRangeFromPoint) { // Chrome
                range=document.caretRangeFromPoint(e.clientX,e.clientY);
            }
            else if (e.rangeParent) { // Firefox
                range=document.createRange(); range.setStart(e.rangeParent,e.rangeOffset);
            }
            var sel = window.getSelection();
            sel.removeAllRanges(); sel.addRange(range);

            $(sel.anchorNode).closest(DD.$dropzones.selector).get(0).focus(); // essential
            document.execCommand('insertHTML',false,'<param name="dragonDropMarker" />'+DD.dropLoad);
            sel.removeAllRanges();

            // verification with dragonDropMarker
            var $DDM=$('param[name="dragonDropMarker"]');
            var insertSuccess = $DDM.length>0;
            if (insertSuccess) {
                $(DD.$draggables.selector).filter('[dragged]').remove();
                $DDM.remove();
            }

            DD.dropLoad=null;
            DD.bindDraggables();
            e.preventDefault();
        });
    };
    DD.disengage=function(){
        DD.$draggables=$( DD.$draggables.selector ); // reselections
        DD.$dropzones=$( DD.$dropzones.selector );
        DD.$noDrags=$( DD.$noDrags.selector );
        DD.$draggables.removeAttr('draggable').removeAttr('dragged').off('dragstart');
        DD.$noDrags.removeAttr('draggable');
        DD.$dropzones.removeAttr('droppable').off('dragenter');
        DD.$dropzones.off('drop');
    };
    if (o) DD.engage(o);
}



$(function(){

    window.DragonDrop = new DRAGON_DROP({
        draggables:$('.fancy'),
        dropzones:$('[contenteditable]'),
        noDrags:$('.fancy img')
    });

    // This is just the enable/disable contenteditable button at the bottom of the page.
    $('button[name="toggleContentEditable"]').click(function(){
        var button=this;
        $('[contenteditable]').each(function(){
            if ($(this).attr('contenteditable')==='true') {
                $(this).attr('contenteditable','false');
                $(button).html('enable contenteditable');
            } else {
                $(this).attr('contenteditable','true');
                $(button).html('disable contenteditable');
            }
        });
    });

});

フィドル:

http://jsfiddle.net/ChaseMoskal/T2zHQ/

于 2013-08-31T16:36:43.643 に答える
-1

jQuery UI の「ドラッグ可能」機能について誰も言及していないことに驚いています。独自のものを構築しようとするのではなく、非常に強力でカスタマイズ可能です。

于 2013-08-28T10:11:19.697 に答える
-1

ピノキオ、ドロップする前に何らかのプレビュー ドロップ可能なデザイン (新しい onmouseover/mouseenter プレビュー) を作成することをお勧めします。

getFromPoint (クロス ブラウジング サポート用) の使用も放棄し、代わりに JQuery の mouseenter / mouseleave を使用して、マウスアップ/ドロップの時間のホバリング要素を割り当てます。

ホバリング要素と、ドキュメントに対するその位置 (jQquery.position) を使用すると、要素を追加または先頭に追加するために、カーソル位置に基づいて何らかの方法で計算を行うことができます。

それが役に立てば幸い

于 2013-08-24T20:08:01.657 に答える