MWT
MWT

Reputation: 172

Drag elements to specific position using jquery or simple js

First of all I am afraid for asking this question because you can rate me bad but I have to ask. I will delete the question by myself just let me know in comments.

I want to move the elements to specific position when user move the an element to right 25px then it should automatically move to the right element like new ul > li should be created in its a first 2nd level of element then if someone drag this element which has moved more to the 25px right then it should also move to the right and it should be the 3rd level and so on. and also to the left side drag to revert to the orignal position I want to make a tree structure like this when someone drag the element to the right or left then

[----1--1st level--]
[----2--1st level--]
  [----1--2nd level--]
    [----1--3rd level--]
    [----2--3rd level--]
      [----1--4th level--]
  [----2--2nd level--]
    [----1----]
    [----2----]
    [----3----]

how can I do that? is there any js library? Thanks

Upvotes: 3

Views: 1103

Answers (2)

Ramin Bateni
Ramin Bateni

Reputation: 17415

I created & provided this idea here on stackoverflow before.

I know it can be improved and make a better version of it but now you can use it and enjoy....

It's features:

»» Support keyboard arrow keys to move the nodes (Left & Right)

»» Support dragging the nodes by Mouse to Right & Left

»» Save level data of each node in DOM and show it on each node

you can test it on fiddle


Result of my codes:

enter image description here

JS & jQuery

/*Code by Ram >> https://stackoverflow.com/users/1474613/ram */

(function ($) {
    $.Noder = function (oneOfNodes) {
        this.element = '';
        oneOfNodes=(oneOfNodes instanceof $) ? oneOfNodes : $(oneOfNodes)
        this.baseX=oneOfNodes.position().left;
        this.currentX=0;
    };

    $.Noder.prototype = {
        InitEvents: function () {
            //`this` references the created instance object inside an instace's method,
            //however `this` is set to reference a DOM element inside jQuery event handler functions' scope.
            //So we take advantage of JS's lexical scope and assign the `this` reference to
            //another variable that we can access inside the jQuery handlers
            var that = this;
            //I'm using `document` instead of `this` so it will catch arrow keys
            //on the whole document and not just when the element is focused.
            //Also, Firefox doesn't fire the keypress event for non-printable characters
            //so we use a keydown handler
            $(document).keydown(function (e) {
                var key = e.which;
                if (key == 39) {
                    that.moveRight();
                } else if (key == 37) {
                    that.moveLeft();
                }
            });},
        setElement: function(element){
            this.element = (element instanceof $) ? element : $(element);
            console.log(this.element);
            this.currentX=this.element.position().left;
            console.log('currentX: '+this.currentX);
            this.element.addClass('active');
        },
        moveRight: function () {
            console.log('bseX: '+this.baseX);
            console.log('currentX: '+this.currentX);
            var max=(25*2)+this.baseX;
            console.log('max: '+max);
            if(this.currentX<max)
            {
                this.element.css("left", '+=' + 25);
                this.currentX=this.element.position().left;
                setElementLevel(this.element,this.currentX,this.baseX);
                console.log('currentX: '+this.currentX);
            }
        },
        moveLeft: function () {
            if(this.currentX>this.baseX)
            {
                this.element.css("left", '-=' + 25);
                this.currentX=this.element.position().left;
                setElementLevel(this.element,this.currentX,this.baseX);
                console.log('currentX: '+this.currentX);
            }
        }

    };
    $.Noder.defaultOptions = {
        currentX: 0
    };

}(jQuery));

function setElementLevel(element,currentX,baseX){
    var level=0;
    if (currentX==baseX+25)
        level=1;
    else if(currentX==baseX+25+25)
        level=2;
    element.data('level', level);
    setLevelOnElement(element);
}

function getElementLevel(element){
    console.log(element.data('level'));
    return element.data('level');
}

function setLevelOnElement(element){  
    var level = 0;
    if(typeof getElementLevel(element) !=='undefined')
        level = getElementLevel(element);
    console.log('my level: '+level);
    var $levelElement=element.find( ".node-level" );
    if ($levelElement && $levelElement.length>0)
    {
        $levelElement=$($levelElement[0]); 
        console.log($levelElement);
        $levelElement.html(level);
    }
}

var noder = new $.Noder($("#myTree>.node")[0]);

$("#myTree>.node").on('click',function(){
    $("#myTree>.node").each(function(){
        $(this).removeClass('active')
    });    
    console.log($(this)[0].id +' clicked')

    noder.setElement($(this)[0]);

})
noder.InitEvents();


$(document).ready(function() {
    var $dragging = null;
    var $myTree=$('ul#myTree');
    var $oneOfNodes=null;
    var baseX=0;
    if($myTree.length>0)
    {
        console.log($myTree);
        $oneOfNodes=$($myTree.children()[0]);
        console.log($oneOfNodes);
        baseX=$oneOfNodes.position().left;
        console.log('baseX >> '+baseX);
        console.log($myTree);
        var x=0;
        $('ul#myTree').find('li').each(function(){
            x++;
            console.log(x);
            setLevelOnElement($(this));
        });
    }

    $(document.body).on("mousemove", function(e) {
        if ($dragging) {
            var currentX=$dragging.position().left;
            if(e.pageX>(baseX+25) && e.pageX<(baseX+(2*25)))
            {
                $dragging.offset({left: (baseX+25)});
                setElementLevel($dragging,currentX,baseX);
            }
            else if((e.pageX)>(baseX+(2*25)) )
            {
                $dragging.offset({left: (baseX+(2*25))});
                setElementLevel($dragging,currentX,baseX);
            }
            else if(e.pageX<(baseX+25) )
            {
                $dragging.offset({left: (baseX)});
                setElementLevel($dragging,currentX,baseX);
            }
        }
    });

    $(document.body).on("mousedown",  function (e) {
        var $myTree=$('ul#myTree');
        if($(e.target) && $myTree && $myTree.length>0)
        {
            var $li=$(e.target).parent();
            var $ul=$(e.target).parent().parent();
            if ( $ul.is($myTree) && $(e.target).hasClass("node-level") )
            {
                $ul.find('li').each(function(){
                    $(this).removeClass('active');
                });
                $li.addClass('active');
                $dragging = $($li);
            }
        }
    });

    $(document.body).on("mouseup", function (e) {
        $dragging = null;
    });
});

HTML

<ul id="myTree">
    <li class="node" id="node1">
        <span class="node-level"></span>
        <span class="node-content">Node 1</span>
    </li>
    <li class="node" id="node2">
        <span class="node-level"></span>
        <span class="node-content">Node 2</span>
    </li>
    <li class="node" id="node3">
        <span class="node-level"></span>
        <span class="node-content">Node 3</span>
    </li>
    <li class="node" id="node4">
        <span class="node-level"></span>
        <span class="node-content">Node 4</span>
    </li>
    <li class="node" id="node5">
        <span class="node-level"></span>
        <span class="node-content">Node 5</span>
    </li>
    <li class="node" id="node6">
        <span class="node-level"></span>
        <span class="node-content">Node 6</span>
    </li>
    <li class="node" id="node7">
        <span class="node-level"></span>
        <span class="node-content">Node 7</span>
    </li>
</ul>

CSS

#myTree{
    margin:20px;
    padding:0;
    list-style-type:none;
    font-size:11px;
}
#myTree>li>.node-level{
    padding:6px 10px;
    color:#ddd;
    background:gray;
    position:relative;
    cursor:move;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
#myTree>li>.node-content{
    padding:5px; 15px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
#myTree>.node:hover {
    background:#acd;
    color:#004;
}
#myTree>.node.active {
    border:1px #a66 solid;
    background:#fd8;
    color:#004;
}
#myTree>li.node {
    width:151px;
    background:#ddd;
    margin-top:2px;
    padding:5px 0px 5px 0;
    color:#555;
    cursor:pointer;
    position:relative;
    border:1px solid #ccc;
}
html, body {
    height:100%;
}
div { width:151px;
    background:#ddd;
    margin-top:2px;
    padding:5px 15px;
    color:#555;
    cursor:pointer;
    position:relative;
    border:1px solid #ccc; }

Upvotes: 2

yuvi
yuvi

Reputation: 18427

You can use the html5sortable library to create a tree structure draggable list by using the connectWith option:

$('.sortable').sortable({
  handle: ".handle",
  connectWith: '.connected'
});
* {
  font-family: Ubuntu, Sans-serif;
}
ul li {
  margin: 15px;
}
ul li .handle {
  cursor: pointer;
}
<link href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/html5sortable/0.1.1/html.sortable.min.js"></script>

<ul class="sortable connected">
  <li><i class="fa fa-arrows handle"></i> First Level Foo</li>
  <li><i class="fa fa-arrows handle"></i> First Level Bar</li>
  <li>
    <ul class="sortable connected">
      <li><i class="fa fa-arrows handle"></i> Second Level</li>
    </ul>
  </li>
</ul>

The only downside is that it's a pretty clean library - it doesn't do a whole lot besides dragging, so everything else (dragging outside, creating new lists, etc.) you need to implement on your own (for example, in the snippet above if you drag all the items out of the inner list it "disappears" and you can't drag back to it).

But that being said, it shouldn't prove too difficult, as html5sortable has a neat little reload method so you can just call it every time you add a new .sortable element to the div:

$('.sortable').sortable('reload');

I just did something similar not long ago (I also used the finderSelect plugin with it to create a file-manager like behavior, although that might be overkill if you don't need all that).

Upvotes: 1

Related Questions