https://codepen.io/ypppp/pen/poZGmmp
html
1dropindex: <span id="output2">0</span>, status: <span id="output3">idle</span>2<div class="main">3 <!-- Element.prototype.after用 -->4 <div class="dummy"></div>5 <div class="row">6 <div class="bars" draggable="true"><div></div></div>7 <img src="https://www.gravatar.com/avatar/4a597f2aa8f7a369cdfb6422b4efef03?d=identicon">8 <a href="javascript:void(0)">Tarou</a>9 </div>10 <div class="row">11 <div class="bars" draggable="true"><div></div></div>12 <img src="https://www.gravatar.com/avatar/151269ed16a2dd18e9a3ecfcdfe76fc9?d=identicon">13 <a href="javascript:void(0)">Tanaka</a>14 </div>15 <div class="row">16 <div class="bars" draggable="true"><div></div></div>17 <img src="https://www.gravatar.com/avatar/fec6c2f9cee65487e83664e88a1d87a9?d=identicon">18 <a href="javascript:void(0)">Ishiyama</a>19 </div>20 <div class="row">21 <div class="bars" draggable="true"><div></div></div>22 <img src="https://www.gravatar.com/avatar/dfddacc86e582d2ba88d64b69141fc7b?d=identicon">23 <a href="javascript:void(0)">Watanabe</a>24 </div>25 <div class="row">26 <div class="bars" draggable="true"><div></div></div>27 <img src="https://www.gravatar.com/avatar/6ba569cf685f7e79518e27f73ed2c611?d=identicon">28 <a href="javascript:void(0)">Aragaki</a>29 </div>30 <div class="row">31 <div class="bars" draggable="true"><div></div></div>32 <img src="https://www.gravatar.com/avatar/26843d78eb4ba532d14069affdb88a20?d=identicon">33 <a href="javascript:void(0)">Fujiwara</a>34 </div>35 <div class="row">36 <div class="bars" draggable="true"><div></div></div>37 <img src="https://www.gravatar.com/avatar/5ab508b9384dc3bcfa1d4741fd6879ca?d=identicon">38 <a href="javascript:void(0)">Satou</a>39 </div>40 <div class="dropimage"></div>41</div>
css
1body {2 user-select: none;3 overflow-y: hidden;4}5.main {6 border: black 1px solid;7 width: 300px;8 padding: 5px 10px;9 display: flex;10 flex-direction: column;11 overflow-y: hidden;12 position: relative;13}14.dummy {15 display: none;16}17.row {18 display: flex;19 align-items: center;20 gap: 10px;21 width: calc(100% - 22px);22 height: 30px;23 background-color: white;24 padding: 5px 0;25}26.row > * {27 height: 30px;28}29.bars {30 width: 30px;31 cursor: s-resize;32 display: flex;33 align-items: center;34 justify-content: center;35}36.bars > div {37 fill: black;38 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMCA5NkMwIDc4LjMgMTQuMyA2NCAzMiA2NEg0MTZjMTcuNyAwIDMyIDE0LjMgMzIgMzJzLTE0LjMgMzItMzIgMzJIMzJDMTQuMyAxMjggMCAxMTMuNyAwIDk2ek0wIDI1NmMwLTE3LjcgMTQuMy0zMiAzMi0zMkg0MTZjMTcuNyAwIDMyIDE0LjMgMzIgMzJzLTE0LjMgMzItMzIgMzJIMzJjLTE3LjcgMC0zMi0xNC4zLTMyLTMyek00NDggNDE2YzAgMTcuNy0xNC4zIDMyLTMyIDMySDMyYy0xNy43IDAtMzItMTQuMy0zMi0zMnMxNC4zLTMyIDMyLTMySDQxNmMxNy43IDAgMzIgMTQuMyAzMiAzMnoiLz48L3N2Zz4=");39 background-repeat: no-repeat;40 width: 15px;41 height: 15px;42}43.row img {44 width: 30px;45 pointer-events: none;46}47.row a {48 text-decoration: none;49 color: #c000f5;50}51.row a:hover {52 text-decoration: underline;53}54.dragging {55 position: absolute;56}57 58.dropimage {59 width: calc(100% - 7px);60 height: 37px;61 border: 2px dashed grey;62 display: none;63 z-index: 99;64}65.dropimage-show {66 display: block;67}
js
1(() => {2HTMLCollection.prototype.forEach = function(fn) {3 Array.prototype.forEach.call(this, fn);4}5 6let main, users, height, top, clientY, dropindex, drop, bars;7window.addEventListener("DOMContentLoaded", e => {8 main = document.getElementsByClassName("main")[0];9 // すべての横目10 users = document.getElementsByClassName("row");11 // 項目の一つの高さ12 height = users[0].getBoundingClientRect().height;13 // 画面上からの高さ14 top = users[0].getBoundingClientRect().top;15 clientY = main.getBoundingClientRect().top;16 // ドロップ先に出るイメージ要素17 drop = document.getElementsByClassName("dropimage")[0];18 19 // ハンバーガーボタン20 bars = document.getElementsByClassName("bars");21 bars.forEach((value, index) => {22 value.setAttribute("data-index", index);23 24 value.addEventListener("dragstart", dragstart);25 value.addEventListener("drag", dragging);26 value.addEventListener("dragend", dragend);27 })28});29 30function dragstart(e) {31 // ハンバーガーボタンを非表示にする32 e.target.style.opacity = 0;33 34 // position: absolute追加 [[原因はおそらくここ]]35 e.target.parentElement.classList.add("dragging");36 37 // デバッグ用38 console.clear();39 document.getElementById("output3").innerText = "dragstart";40 console.log("dragstart");41}42 43function dragging(e) {44 // バグ?対策45 if(e.clientY == 0) return;46 47 // ハンバーガーボタンを表示する48 e.target.style.opacity = null;49 50 // ドラッグを視覚的にわかりやすくするため51 e.target.parentElement.style.top = (e.clientY - top - 10) + "px";52 53 // カーソルの座標からドロップ先のインデックスを計算54 dropindex = Math.floor((e.clientY - clientY) / height);55 56 // 範囲外対策57 if(dropindex < 0) dropindex = 0;58 if(dropindex >= users.length - 1) dropindex = users.length - 1;59 60 // ドラッグしている自身の要素を無視する61 if(dropindex >= Number(e.target.getAttribute("data-index"))) dropindex++;62 // ドロップ先に出る要素の位置を変更する63 document.querySelectorAll(".dummy, .row")[dropindex].after(drop);64 65 // ドロップ先に出る要素表示66 drop.classList.add("dropimage-show");67 68 // デバッグ用69 document.getElementById("output2").innerText = dropindex;70 document.getElementById("output3").innerText = "dragging";71 console.log("dragging");72}73 74function dragend(e) {75 // ハンバーガーボタンを表示76 e.target.style.opacity = null;77 e.target.parentElement.style.top = null;78 79 // position: absolute削除80 e.target.parentElement.classList.remove("dragging");81 82 // 実際にドラックした要素をドロップ先に移動する83 document.querySelectorAll(".dummy, .row")[dropindex].after(e.target.parentElement);84 85 // インデックスを振りなおす86 bars.forEach((value, index) => {87 value.setAttribute("data-index", index);88 });89 90 // ドロップ先に出るイメージ要素を非表示にする91 drop.classList.remove("dropimage-show");92 // ドロップ先に出るイメージ要素を一番下に移動する93 main.children[main.children.length - 1].after(drop);94 95 // デバッグ用96 document.getElementById("output3").innerText = "dragend";97 console.log("dragend");98}99})();
上記はハンバーガーボタンをドラッグアンドドロップで要素の並び替えを可能にしたものです。
一番上はドラッグアンドドロップで実際に移動できますが、
2番目以降(※たまに動作するときがあります)は移動しようとしてもドラッグの途中で強制的に"dragend"イベントが発行されます。
いろいろ考えてみましたが、2番目以降もイベントの登録はされており、なぜ"drag"イベントが無視され、"dragend"イベントが強制的に発行されるかの理由がわかりません。
どなたか原因がわかる方はいらっしゃいますか?
補足:
css
1.dragging {2 position: absolute;3}
をコメントアウトすると"drag"イベントは無視されなくなります。おそらく、座標が関係しているものだと思いますが、具体的な解決方法が思い浮かびません。
0 コメント