Q&A
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"イベントは無視されなくなります。おそらく、座標が関係しているものだと思いますが、具体的な解決方法が思い浮かびません。
回答1件
あなたの回答
tips
プレビュー
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
2023/02/08 13:17 編集
2023/02/08 23:25
2023/02/11 07:33
2023/02/12 23:01
2023/02/15 15:17