Бросать во все возможные места

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

Итак, сначала я начал с создания перетаскиваемого основного бита, куда вы можете перетаскивать элемент, затем, когда вы отбрасываете элемент, я использую document.elementFromPoint() чтобы получить элемент, в котором находится позиция курсора (примечание: мне нужно скрыть перетаскиваемый элемент, в противном случае он вернет это).

Теперь у меня есть элемент, ближайший к курсору, основная проблема в том, чтобы выяснить, куда нужно перетаскивать перетаскиваемый элемент по отношению к этому элементу, потому что есть 4 опции append - prepend - insertBefore - insertAfter. Мне удалось получить append - prepend & insertBefore работающий, но он недостаточно надежный, потому что все, что я собираюсь, это использовать высоту и смещение цели, чтобы определить, append or prepend и я увеличиваю параметр Y в getFromPoint чтобы увидеть, если я нажмите другой элемент на коротком расстоянии, чтобы определить insertBefore. Вот код, который у меня есть.

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 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.

Ссылка на странный раздел.

Вот как это работает (предупреждение, это не хорошо). Когда я получаю целевой элемент с помощью elementFromPoint я тогда создаю цикл, который будет elementFromPoint цикл 15 раз и увеличивать значение Y которое входит в elementFromPoint поэтому в основном elementFromPoint перемещается вниз на 15px и если он попадает в новый элемент в пределах этого короткого пространства, я предполагаю что вы хотите вставить элемент перед целью.

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

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

Я также хотел бы отметить, что мое приложение не поддерживает и не будет поддерживать старые браузеры, т.е. IE6, IE7, IE8, IE9.

Ответ 1

ChaseMoskal имеет хорошее решение проблемы, используя contenteditable = "true":

HTML

<!--====  DragonDrop Demo HTML
Here we have two contenteditable <div> -- 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/

Ответ 2

Я бы рассмотрел использование jQuery UI Сортируемый виджет. Пример портлетов обрабатывает требование insertBefore и insertAfter. Я создал простую fiddle, которая основывается на примере портлетов и также удовлетворяет требованиям prepend и append.

Это только начало для вас, что я уверен, что вы можете манипулировать по мере необходимости. connectWith важно в зависимости от того, где вы хотите разрешить размещение вещей.

fiddle

JS

$(".column").sortable({
    items: ".portlet",
    connectWith: ".column"
});
$(".portlet").sortable({
    items: ".portlet-content",
    connectWith: ".portlet"
});
$(".column").disableSelection();

HTML

<div class="column">
    <div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
        <div class="portlet-content">One. Lorem ipsum dolor sit amet, consectetuer adipiscing elit</div>
    </div>
    <div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
        <div class="portlet-content">Two. Lorem ipsum dolor sit amet, consectetuer adipiscing elit</div>
    </div>
    <div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
        <div class="portlet-content">Three. Lorem ipsum dolor sit amet, consectetuer adipiscing elit</div>
    </div>
</div>
<div class="column">
    <div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
        <div class="portlet-content">Four. Lorem ipsum dolor sit amet, consectetuer adipiscing elit</div>
    </div>
    <div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
        <div class="portlet-content">Five. Lorem ipsum dolor sit amet, consectetuer adipiscing elit</div>
    </div>
</div>

Ответ 3

interact.js - это автономный, легкий модуль перетаскивания и изменения размера javascript для мобильных и настольных (включая IE8 +) с поддержкой взаимодействуя с элементами HTML и SVG. Он только захватывает и вычисляет вход пользователя drag, и он оставляет все стили и визуальную обратную связь до вас.

Я обновил скрипт JS с рабочей демонстрацией: http://jsfiddle.net/mLX5A/6/

var box = document.getElementById('box'),
    container = document.getElementById('container');

// make an Interactable of the box
interact(box)
// make a draggable of the Interactable
.draggable(true)
    .on('dragmove', function (event) {
        event.target.x |= 0;
        event.target.y |= 0;

        event.target.x += event.dx,
        event.target.y += event.dy;

        // translate the element by the change in pointer position
        event.target.style[transformProp] =
            'translate(' + event.target.x + 'px, ' + event.target.y + 'px)';
    });

// Then to make #container a dropzone dropzone:
interact('#container')      // or interact(document.getElementById('container'))
    .dropzone(true)
    .on('drop', function (event) {
        // target is the dropzone, relatedTarget was dropped into target

        event.relatedTarget.x = 0;
        event.relatedTarget.y = 0;
        event.relatedTarget.style[transformProp] = '';

        var siblings = container.querySelectorAll('p'),
            len = siblings.length;

        for (var i = 0; i < len; i++) {
            var rect = interact(siblings[i]).getRect();

            if (event.pageY < rect.top) {
                return siblings[i].parentNode
                    .insertBefore(event.relatedTarget, siblings[i]);
            }
        }

        return container.appendChild(event.relatedTarget);
    });

// CSS transform vendor prefixes
transformProp = 'transform' in document.body.style ?
    'transform' : 'webkitTransform' in document.body.style ?
    'webkitTransform' : 'mozTransform' in document.body.style ?
    'mozTransform' : 'oTransform' in document.body.style ?
    'oTransform' : 'msTransform';

Ответ 4

Я пытался сделать то, что знаю.

<div class="container">
    <div id="box"></div>
     <div class="draggable"> alsdf dsalf asdfsadf
    dsaf sadfldsaf sadkf sadlfsadf
    asdf safdsafdksadf sadf lasldkfjsaldf safdsa
    dfsadflsadf asdlfsafdsafdsa
    fsafdsadf safdls
    </div>
    <div class="draggable"> alsdf dsalf asdfsadf
    dsaf sadfldsaf sadkf sadlfsadf
    asdf safdsafdksadf sadf lasldkfjsaldf safdsa
    dfsadflsadf asdlfsafdsafdsa
    fsafdsadf safdls
    </div>
    <div class="draggable"> alsdf dsalf asdfsadf
    dsaf sadfldsaf sadkf sadlfsadf
    asdf safdsafdksadf sadf lasldkfjsaldf safdsa
    dfsadflsadf asdlfsafdsafdsa
    fsafdsadf safdls
    </div>
    <div class="draggable"> alsdf dsalf asdfsadf
    dsaf sadfldsaf sadkf sadlfsadf
    asdf safdsafdksadf sadf lasldkfjsaldf safdsa
    dfsadflsadf asdlfsafdsafdsa
    fsafdsadf safdls
    </div>
</div>

script

var drag = new function(){
      this.box;
      this._cnt;
      this.hvEle;
      this.trgEle;
      this.initX;
      this.initY;
      this.dragging=false;
};
$(document).on('mousedown','#box',function(){
    drag.box = $(this);  
    initX = drag.box.offset().left;
    initY = drag.box.offset().top;

    drag._cnt = drag.box.closest('.container');
    drag.hvEle = drag._cnt.find('.draggable');
    drag.box.css({position:"absolute"});

    drag.hvEle.mousemove(function(e){
        e.stopPropagation();
        drag.dragging=true;
        drag.box.css({cursor:"move"});
        drag.hvEle.removeClass('dragHv');
        drag.trgEle = $(this);
        drag.trgEle.addClass('dragHv');
        var x =  e.pageX ||e.clientX;
        var y =  e.pageY ||e.clientY;
        drag.box.css({left:x,top:y});

    });

    drag.box.mouseup(function(e){        
         drag.trgEle.append(drag.box);
        /***** You can write your required logic here to either append or
         insertBefore or insertAfter
        ****/
        drag.box.css({position:'static',cursor:"move"}); 
         drag.hvEle.unbind('mousemove');
         drag.box.unbind('mouseup');
         drag.dragging=false;
     });
});
$('.container').disableSelection();

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

$(document).on('mousemove','html',function(){
    if(drag.dragging){
        drag.box.css({cursor:"no-drop"});
    }
});
$(document).on('mouseup','html',function(){
    if(drag.dragging){
        drag.box.css({left:drag.initX,top:drag.initY,position:"static"});
        drag.box.css({cursor:"move"});
        drag.hvEle.unbind('mousemove');
        drag.box.unbind('mouseup');
        dragging=false;
    }
});
**Styles****

    .container{
      width:100%;
      height:100%;    
      border:1px solid green;
    }
    .draggable{
     padding:10px 5px;
    }
    .draggable:nth-child(even){
      background:#efefef;
      border-bottom:1px solid black;
      border-top:1px solid black;
    }
    #box{
     height:50px;
     width:200px;
     background:red;
     cursor:move;   
    }
    .dragHv{
        background-color:yellow !important;
    }

Вот fiddle.
Я надеюсь, что это поможет вам хотя бы каким-то образом.

Ответ 5

см. этот пример, это может помочь вам

<html>

<head>



<script language="javascript" type="text/javascript"><!--

    var dragitem = undefined;



    function setdragitem(item, evt) {

        dragitem=item;

        // alert('item: '+item);

        // item is an HTML DIV element.

        // evt is an event.



        // If the item should not be draggable, enable this next line.

        // evt.preventDefault();



        return true;

    }

    function cleardragitem() {

        dragitem=undefined;

        // alert('item: '+item);

    }

    function dodrag() {

        // alert('item: '+dragitem);

    }



    // This is required---used to tell WebKit that the drag should

    // be allowed.

    function handledragenter(elt, evt) {

        evt.preventDefault();

        return true;

    }

    function handledragover(elt, evt) {

        evt.preventDefault();

        return true;

    }





    function handledragleave(elt, evt) {



    }



    function handledrop(elt, evt) {

        // alert('drop');

        dragitem.style.display="none";

        var newid=dragitem.id + '_dest';

        var dest = document.getElementById(newid);

        dest.innerHTML = dragitem.innerHTML;

    }





// --></script>



<style type="text/css"><!--



    .wordbox { border: 1px solid black; text-align: center; width: 50px; float: left; -webkit-user-drag: element; -webkit-user-select: none; }

    .spacer { clear: both; }

    .target { margin-top: 30px; padding: 30px; width: 70px; border: 1px solid black; background: #c0c0ff; margin-bottom: 30px; -webkit-user-drop: element; }

    .word   { margin: 30px; min-height: 30px; border-bottom: 1px solid black; width: 50px;  float: left; }



--></style>



</head>

<body>



<p>Drop words onto target area to put them in their places.</p>



<div class='wordbox' id='this' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>This</div>

<div class='wordbox' id='is' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>is</div>

<div class='wordbox' id='a' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>a</div>

<div class='wordbox' id='test' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>test</div>



<div class='spacer'></div>

<div class='target' ondragenter='handledragenter(this, event);' ondragover='handledragover(this, event);' ondragleave='handledragleave(this, event);' ondrop='handledrop(this, event);'>TARGET</div>



<div class='words'>

<div class='word' id='this_dest'></div>

<div class='word' id='is_dest'></div>

<div class='word' id='a_dest'></div>

<div class='word' id='test_dest'></div>

</div>





</body>

</html>

для более см. эту ссылку

иначе см. эту ссылку

Ответ 6

Пиноккио, я бы предложил вам создать какой-то проект с возможностью просмотра, прежде чем вы упадете (новый просмотр onmouseover/mouseenter), я bielive, который помог бы вам в envolve ваш алгоритм.

Я также отказался бы от использования getFromPoint (для поддержки кросс-браузера), вместо этого я бы использовал JQuery mouseenter/mouseleave, чтобы назначить элемент зависания времени для mouseup/dropping.

С зависающим элементом в руке, а также его положением (jQquery.position) относительно документа вы можете каким-то образом сделать свои вычисления на основе позиции курсора для добавления или добавления элемента.

Надеюсь, что это поможет