Search code examples
jqueryjquery-uidraggable

Draggable element bounce around sometimes


I've created this snippet below, and I've added draggable from jquery-ui to it. I general it works just fine, but sometimes when you drag the elements around they start "bounzing".

Try it in "Full page" then it happens

Can anyone explain why this happends.?

This is the demo

function loadTM(obj){
    var $this = obj; 
}

$(".add_list_text").on("click",function(){
    $(this).parent().addClass("active")
})

$("#add_list button").on("click",function() {
    if($(this).prev("input").val().length > 0)
    {
        var v = $(this).prev("input").val();
        $(".board_content").prepend(NewTask.format(v));
        $(this).prev("input").val("");
        $(".add_list.active").removeClass("active");
        runDrag();
    }
})

$(document).on("click",".list_head .title",function() {
    $(this).attr("contenteditable",true).focus() 
})

$(document).on("click",".list_footer .add_card",function(){
    $(".list_footer.active .add_card").val("")
    $(".list_footer.active").removeClass("active");
    $(this).closest(".list_footer").addClass("active")
})

$(document).on("click",".list_footer .add_card_form .add_card_button",function(){
    var v = $(this).closest(".add_card_form").find("textarea").val();
    $(this).closest(".list_c").find(".list_body").append(NewCard.format(v));
    $(this).closest(".add_card_form").find("textarea").val("");
    $(this).closest(".list_footer.active").removeClass("active")
})

$(".board_content").on("click",function(e) {
    if((e.target.id != "add_list" && 
        $(e.target).parents("#add_list").length == 0) && 
        $(".add_list.active").length > 0){
        $(".add_list.active").removeClass("active");
    }

    if((Array.from(e.target.classList).indexOf('title') == -1 &&
        $(e.target).parents("#add_list").length == 0)){
        $(".list_head .title[contenteditable=true]").attr("contenteditable",false)
    }
})






var NewTask = '<div class="list">'+
'<div class="list_c">'+
'<div class="list_head">'+
'<div class="title" contenteditable="false">{0}</div>'+
'</div>'+
'<div class="list_body"></div>'+
'<div class="list_footer">'+
'<div class="add_card_form">'+
'<textarea></textarea>'+
'<button type="button" class="btn btn-success add_card_button">Add</button>'+
'</div>'+
'<div class="add_card">Add a card..</div>'+
'</div>'+
'</div>'+
'</div>';

var NewCard = '<div class="card">'+
'<div class="card_title">{0}</div>'+
'</div>';


String.prototype.format = String.prototype.f = function() {
    var s = this,
        i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};

$(document).ready(function(e) {
    $(".list_footer .add_card_form textarea").keypress(function(event) {
      if(e.which == '13') {
        return false;
      }
    });
    runDrag();
});

function runDrag(){
    $( ".list" ).sortable({
        revert: true
      });
    $( ".list_c" ).draggable({ 
        connectToSortable: ".list",
        revert: "invalid",
        start: function( event, ui ) {
            $(".list").addClass("dragChild");
        },
        stop: function( event, ui ) {
            $(".list").removeClass("dragChild");
        }
    });
}
.TM_board {
  background-color: #0079bf;
}
.TM_board .board_header {
  text-align: center;
  color: #7FB3D1;
  padding: 5px 8px;
  height: 30px;
  background-color: #0067A3;
}
.TM_board .board_header .board_header_left {
  float: left;
}
.TM_board .board_header .board_header_center {
  display: inline-block;
  font-weight: bold;
  font-style: italic;
}
.TM_board .board_header .board_header_right {
  float: right;
}
.TM_board .board_content {
  user-select: none;
  white-space: nowrap;
  margin-bottom: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 10px;
  padding-right: 56px;
  min-height: 70vh;
  padding-top: 10px;
  display: flex;
}
.TM_board .board_content .list {
  width: 270px;
  margin: 0 5px;
  box-sizing: border-box;
  display: inline-block;
  vertical-align: top;
  white-space: nowrap;
}
.TM_board .board_content .list.dragChild {
  background: rgba(0, 0, 0, 0.12); 
}
.TM_board .board_content .list .list_c {
  background: #E2E4E6;
  border-radius: 3px;
  box-sizing: border-box;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  max-height: 100%;
  position: relative;
  white-space: normal;
  margin-bottom: 8px;
}
.TM_board .board_content .list .list_c .list_head {
  padding: 5px 8px;
  cursor: pointer;
}
.TM_board .board_content .list .list_c .list_body {
  padding: 5px 8px 0 8px;
}
.TM_board .board_content .list .list_c .list_body .card {
  background-color: #fff;
  border-bottom: 1px solid #ccc;
  border-radius: 3px;
  cursor: pointer;
  margin-bottom: 6px;
}
.TM_board .board_content .list .list_c .list_body .card .card_title {
  padding: 5px 8px;
  color: #444;
}
.TM_board .board_content .list .list_c .list_footer .add_card_form {
  display: none;
  padding: 5px 8px;
}
.TM_board .board_content .list .list_c .list_footer .add_card_form textarea {
  border: none;
  width: 100%;
  max-width: 100%;
  min-width: 100%;
  min-height: 50px;
  max-height: 230px;
  margin-top: 0px;
  margin-bottom: 0px;
  background-color: #fff;
  border-bottom: 1px solid #ccc;
  border-radius: 3px;
  overflow: hidden;
  word-wrap: break-word;
}
.TM_board .board_content .list .list_c .list_footer .add_card_form button {
  margin-top: 8px;
  padding: 5px 8px;
  min-width: 4rem;
  cursor: pointer;
}
.TM_board .board_content .list .list_c .list_footer .add_card {
  color: #838c91;
  display: block;
  -webkit-box-flex: 0;
  -webkit-flex: 0 0 auto;
  -ms-flex: 0 0 auto;
  flex: 0 0 auto;
  padding: 8px 10px;
  position: relative;
  text-decoration: none;
  user-select: none;
  cursor: pointer;
}
.TM_board .board_content .list .list_c .list_footer .add_card:hover {
  background-color: #CDD2D4;
  color: #4d4d4d;
  text-decoration: underline;
}
.TM_board .board_content .list .list_c .list_footer.active .add_card_form {
  display: block;
}
.TM_board .board_content .list .list_c .list_footer.active .add_card {
  display: none;
}
.TM_board .board_content .add_list {
  background: rgba(0, 0, 0, 0.12);
  cursor: pointer;
  color: #fff;
  height: 100%;
  min-height: 30px;
  padding: 4px;
  border-radius: 3px;
}
.TM_board .board_content .add_list .add_list_text {
  color: rgba(255, 255, 255, 0.7);
  line-height: 30px;
  padding-left: 5px;
  display: block;
}
.TM_board .board_content .add_list input {
  background: rgba(0, 0, 0, 0.05);
  border-color: #aaa;
  box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.15);
  display: none;
  margin: 0;
  width: 100%;
  box-sizing: border-box;
  -webkit-appearance: none;
  border: 1px solid #CDD2D4;
  border-radius: 3px;
  padding: 7px;
  color: #444;
}
.TM_board .board_content .add_list button {
  display: none;
  margin-top: 4px;
  clear: both;
}
.TM_board .board_content .add_list.active {
  background-color: #E2E4E6;
}
.TM_board .board_content .add_list.active .add_list_text {
  display: none;
}
.TM_board .board_content .add_list.active input {
  display: block;
}
.TM_board .board_content .add_list.active button {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="TM_board"> 
    <div class="board_header">
        <div class="board_header_left">search</div>
        <div class="board_header_center">TM</div>
        <div class="board_header_right"></div>
    </div>
    <div class="board_content">
        <div class="list">
            <div class="list_c">
                <div class="list_head">
                    <div class="title" contenteditable="false">Test</div>
                </div>
                <div class="list_body">
                    <div class="card">
                        <div class="card_title">Title</div>
                    </div>
                </div>
                <div class="list_footer">
                    <div class="add_card_form">
                        <textarea></textarea>
                        <button type="button" class="btn btn-success add_card_button">Add</button>
                    </div>
                    <div class="add_card">Add a card..</div>
                </div>
            </div>
        </div>
        <div class="list">
            <div class="list_c">
                <div class="list_head">
                    <div class="title" contenteditable="false">Test</div>
                </div>
                <div class="list_body">
                    <div class="card">
                        <div class="card_title">Title</div>
                    </div>
                </div>
                <div class="list_footer">
                    <div class="add_card_form">
                        <textarea></textarea>
                        <button type="button" class="btn btn-success add_card_button">Add</button>
                    </div>
                    <div class="add_card">Add a card..</div>
                </div>
            </div>
        </div>
        <div id="add_list" class="list add_list">
            <div class="add_list_text">Add a list...</div>
            <input placeholder="Add a list...">
            <button type="button" class="btn btn-success">Save</button>
        </div>
    </div>
</div>


Solution

  • Hard to pinpoint exactly because there are a lot happening on drag for both sortable and draggable and you're mixing the two, but from what I see, I'd say the problem comes from behaviors in draggable with connectToSortable and sortable that are in conflict. A draggable that is connected to a sortable will activate and deactivate sortable based on where you drag it. So it can trigger a sortable stop. But you have elements that are both sortable and draggable, meaning that from the draggable connectTosortable you can trigger a stop on the sortable of the same element. But then your element is a sortable as well, so it stays there somewhere else in the sequence. So you end up with a conflicting behavior.

    You have different options, the most obvious one would be to remove the draggable and only keep sortable and connect the sortables between them. Like this:

    function loadTM(obj){
        var $this = obj; 
    }
    
    $(".add_list_text").on("click",function(){
        $(this).parent().addClass("active")
    })
    
    $("#add_list button").on("click",function() {
        if($(this).prev("input").val().length > 0)
        {
            var v = $(this).prev("input").val();
            $(".board_content").prepend(NewTask.format(v));
            $(this).prev("input").val("");
            $(".add_list.active").removeClass("active");
            runDrag();
        }
    })
    
    $(document).on("click",".list_head .title",function() {
        $(this).attr("contenteditable",true).focus() 
    })
    
    $(document).on("click",".list_footer .add_card",function(){
        $(".list_footer.active .add_card").val("")
        $(".list_footer.active").removeClass("active");
        $(this).closest(".list_footer").addClass("active")
    })
    
    $(document).on("click",".list_footer .add_card_form .add_card_button",function(){
        var v = $(this).closest(".add_card_form").find("textarea").val();
        $(this).closest(".list_c").find(".list_body").append(NewCard.format(v));
        $(this).closest(".add_card_form").find("textarea").val("");
        $(this).closest(".list_footer.active").removeClass("active")
    })
    
    $(".board_content").on("click",function(e) {
        if((e.target.id != "add_list" && 
            $(e.target).parents("#add_list").length == 0) && 
            $(".add_list.active").length > 0){
            $(".add_list.active").removeClass("active");
        }
    
        if((Array.from(e.target.classList).indexOf('title') == -1 &&
            $(e.target).parents("#add_list").length == 0)){
            $(".list_head .title[contenteditable=true]").attr("contenteditable",false)
        }
    })
    
    
    
    
    
    
    var NewTask = '<div class="list">'+
    '<div class="list_c">'+
    '<div class="list_head">'+
    '<div class="title" contenteditable="false">{0}</div>'+
    '</div>'+
    '<div class="list_body"></div>'+
    '<div class="list_footer">'+
    '<div class="add_card_form">'+
    '<textarea></textarea>'+
    '<button type="button" class="btn btn-success add_card_button">Add</button>'+
    '</div>'+
    '<div class="add_card">Add a card..</div>'+
    '</div>'+
    '</div>'+
    '</div>';
    
    var NewCard = '<div class="card">'+
    '<div class="card_title">{0}</div>'+
    '</div>';
    
    
    String.prototype.format = String.prototype.f = function() {
        var s = this,
            i = arguments.length;
    
        while (i--) {
            s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
        }
        return s;
    };
    
    $(document).ready(function(e) {
        $(".list_footer .add_card_form textarea").keypress(function(event) {
          if(e.which == '13') {
            return false;
          }
        });
        runDrag();
    });
    
    function runDrag(){
        $( ".list" ).sortable({
            revert: true,
            items: ".list_c",
            connectWith: ".list",
             start: function( event, ui ) {
                $(".list").addClass("dragChild");
            },
            stop: function( event, ui ) {
                $(".list").removeClass("dragChild");
            }
            
          });
       /* $( ".list_c" ).draggable({ 
            connectToSortable: ".list",
            revert: "invalid",
            start: function( event, ui ) {
                $(".list").addClass("dragChild");
            },
            stop: function( event, ui ) {
                $(".list").removeClass("dragChild");
            }
        });*/
    }
    .TM_board {
      background-color: #0079bf;
    }
    .TM_board .board_header {
      text-align: center;
      color: #7FB3D1;
      padding: 5px 8px;
      height: 30px;
      background-color: #0067A3;
    }
    .TM_board .board_header .board_header_left {
      float: left;
    }
    .TM_board .board_header .board_header_center {
      display: inline-block;
      font-weight: bold;
      font-style: italic;
    }
    .TM_board .board_header .board_header_right {
      float: right;
    }
    .TM_board .board_content {
      user-select: none;
      white-space: nowrap;
      margin-bottom: 10px;
      overflow-x: auto;
      overflow-y: hidden;
      padding-bottom: 10px;
      padding-right: 56px;
      min-height: 70vh;
      padding-top: 10px;
      display: flex;
    }
    .TM_board .board_content .list {
      width: 270px;
      margin: 0 5px;
      box-sizing: border-box;
      display: inline-block;
      vertical-align: top;
      white-space: nowrap;
    }
    .TM_board .board_content .list.dragChild {
      background: rgba(0, 0, 0, 0.12); 
    }
    .TM_board .board_content .list .list_c {
      background: #E2E4E6;
      border-radius: 3px;
      box-sizing: border-box;
      display: flex;
      -webkit-box-orient: vertical;
      -webkit-flex-direction: column;
      -ms-flex-direction: column;
      flex-direction: column;
      max-height: 100%;
      position: relative;
      white-space: normal;
      margin-bottom: 8px;
    }
    .TM_board .board_content .list .list_c .list_head {
      padding: 5px 8px;
      cursor: pointer;
    }
    .TM_board .board_content .list .list_c .list_body {
      padding: 5px 8px 0 8px;
    }
    .TM_board .board_content .list .list_c .list_body .card {
      background-color: #fff;
      border-bottom: 1px solid #ccc;
      border-radius: 3px;
      cursor: pointer;
      margin-bottom: 6px;
    }
    .TM_board .board_content .list .list_c .list_body .card .card_title {
      padding: 5px 8px;
      color: #444;
    }
    .TM_board .board_content .list .list_c .list_footer .add_card_form {
      display: none;
      padding: 5px 8px;
    }
    .TM_board .board_content .list .list_c .list_footer .add_card_form textarea {
      border: none;
      width: 100%;
      max-width: 100%;
      min-width: 100%;
      min-height: 50px;
      max-height: 230px;
      margin-top: 0px;
      margin-bottom: 0px;
      background-color: #fff;
      border-bottom: 1px solid #ccc;
      border-radius: 3px;
      overflow: hidden;
      word-wrap: break-word;
    }
    .TM_board .board_content .list .list_c .list_footer .add_card_form button {
      margin-top: 8px;
      padding: 5px 8px;
      min-width: 4rem;
      cursor: pointer;
    }
    .TM_board .board_content .list .list_c .list_footer .add_card {
      color: #838c91;
      display: block;
      -webkit-box-flex: 0;
      -webkit-flex: 0 0 auto;
      -ms-flex: 0 0 auto;
      flex: 0 0 auto;
      padding: 8px 10px;
      position: relative;
      text-decoration: none;
      user-select: none;
      cursor: pointer;
    }
    .TM_board .board_content .list .list_c .list_footer .add_card:hover {
      background-color: #CDD2D4;
      color: #4d4d4d;
      text-decoration: underline;
    }
    .TM_board .board_content .list .list_c .list_footer.active .add_card_form {
      display: block;
    }
    .TM_board .board_content .list .list_c .list_footer.active .add_card {
      display: none;
    }
    .TM_board .board_content .add_list {
      background: rgba(0, 0, 0, 0.12);
      cursor: pointer;
      color: #fff;
      height: 100%;
      min-height: 30px;
      padding: 4px;
      border-radius: 3px;
    }
    .TM_board .board_content .add_list .add_list_text {
      color: rgba(255, 255, 255, 0.7);
      line-height: 30px;
      padding-left: 5px;
      display: block;
    }
    .TM_board .board_content .add_list input {
      background: rgba(0, 0, 0, 0.05);
      border-color: #aaa;
      box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.15);
      display: none;
      margin: 0;
      width: 100%;
      box-sizing: border-box;
      -webkit-appearance: none;
      border: 1px solid #CDD2D4;
      border-radius: 3px;
      padding: 7px;
      color: #444;
    }
    .TM_board .board_content .add_list button {
      display: none;
      margin-top: 4px;
      clear: both;
    }
    .TM_board .board_content .add_list.active {
      background-color: #E2E4E6;
    }
    .TM_board .board_content .add_list.active .add_list_text {
      display: none;
    }
    .TM_board .board_content .add_list.active input {
      display: block;
    }
    .TM_board .board_content .add_list.active button {
      display: block;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <div class="TM_board"> 
        <div class="board_header">
            <div class="board_header_left">search</div>
            <div class="board_header_center">TM</div>
            <div class="board_header_right"></div>
        </div>
        <div class="board_content">
            <div class="list">
                <div class="list_c">
                    <div class="list_head">
                        <div class="title" contenteditable="false">Test</div>
                    </div>
                    <div class="list_body">
                        <div class="card">
                            <div class="card_title">Title</div>
                        </div>
                    </div>
                    <div class="list_footer">
                        <div class="add_card_form">
                            <textarea></textarea>
                            <button type="button" class="btn btn-success add_card_button">Add</button>
                        </div>
                        <div class="add_card">Add a card..</div>
                    </div>
                </div>
            </div>
            <div class="list">
                <div class="list_c">
                    <div class="list_head">
                        <div class="title" contenteditable="false">Test</div>
                    </div>
                    <div class="list_body">
                        <div class="card">
                            <div class="card_title">Title</div>
                        </div>
                    </div>
                    <div class="list_footer">
                        <div class="add_card_form">
                            <textarea></textarea>
                            <button type="button" class="btn btn-success add_card_button">Add</button>
                        </div>
                        <div class="add_card">Add a card..</div>
                    </div>
                </div>
            </div>
            <div id="add_list" class="list add_list">
                <div class="add_list_text">Add a list...</div>
                <input placeholder="Add a list...">
                <button type="button" class="btn btn-success">Save</button>
            </div>
        </div>
    </div>