こんなものを作ってみたので簡単に解説です
記事を開くと画像の問題がありますが、そこからさらに私の個人サイトに飛んで
実際に文字をドラッグアンドドロップできるクロスワードが遊べます。
HTML(回答エリアと問題)※省略
p5_9th.html
<table class="container">
<tr>
<td></td>
<td class="box" label="A"></td>
</tr>
</table>
<table class="container">
<tr>
<td class="box"><div class="card" draggable="true" id="s1" label=" げ "></div></td>
</tr>
</table>
JavaScript
p5_9th.html
<script type="text/javascript"><!--
// ----- パソコン・スマートフォン共通 -----
let draggedCard = null; // ドラッグ中のカード
function isDroppableBox(element) { // その要素はカードを置けるボックスか
if (!element || !element.classList.contains("box")) return false; // ボックスでないとだめ
if (element.classList.contains("card")) return false; // カードであったらだめ
if (element.childElementCount > 0) return false; // 先客のカードがいたらだめ
return true;
}
// ----- パソコンでのドラッグドロップ用 -----
const handleDragCardStart = (e) => {
// カードをドラッグ開始したときのイベント
e.target.classList.add("dragging");
draggedCard = e.target;
};
const handleDragCardEnd = (e) => {
// カードをドラッグ終了したときのイベント
e.target.classList.remove("dragging");
draggedCard = null;
};
// カードたちにイベントを登録
document.querySelectorAll(".card").forEach(card => {
card.addEventListener("dragstart", handleDragCardStart, false);
card.addEventListener("dragend", handleDragCardEnd, false);
});
const handleDragEnter = (e) => {
// 要素の上に何かがドラッグされてきたときのイベント
if (draggedCard == null) return; // ドラッグされてきたのがカードでないなら何もしない
// ドラッグされてきたのがカードであってそこがカードを置けるボックスなら知らせるため目立たせる
if (isDroppableBox(e.target)) e.target.classList.add("box-over");
};
const handleDragOver = (e) => {
// 要素の上に何かがドラッグされているときのイベント
e.preventDefault(); // ブラウザ既定の処理を禁止
if (draggedCard == null) { // ドラッグされてきたのがカードでないなら禁止カーソルにする
e.dataTransfer.dropEffect = "none";
return;
}
// そこがカードを置けるボックスでないなら禁止カーソルにする
e.dataTransfer.dropEffect = isDroppableBox(e.target) ? "move" : "none";
// 但しそこがドラッグ中のカード自身や元々いたボックス上空なら便宜上移動可能カーソルにする
// ドラッグし始めていきなり禁止マークが出るとインタフェースとして微妙なため
if (e.target == draggedCard) e.dataTransfer.dropEffect = "move";
if (e.target == draggedCard.parentElement) e.dataTransfer.dropEffect = "move";
};
const handleDragLeave = (e) => {
// 要素の上から何かが去っていったときのイベント
e.target.classList.remove("box-over"); // もし目立たせていたら目立たなくする
};
const handleDrop = (e) => {
// 要素の上でカードが手放されたときのイベント
e.preventDefault(); // ブラウザ既定の処理を禁止
e.target.classList.remove("box-over"); // もし目立たせていたら目立たなくする
// ドラッグされてきたのがカードであってそこがカードを置けるボックスならそこにカードを追加
if (draggedCard && isDroppableBox(e.target)) e.target.appendChild(draggedCard);
};
// ページ上に何かがドラッグされてきた/いる/去る/落とされるイベントを登録
document.addEventListener("dragenter", handleDragEnter, false);
document.addEventListener("dragover", handleDragOver, false);
document.addEventListener("dragleave", handleDragLeave, false);
document.addEventListener("drop", handleDrop, false);
// ----- スマートフォンでのドラッグドロップ用 -----
let dragPreview = null; // 指に追従させるカード
const handleTouchStart = (e) => {
// 画面にタッチしたときのイベント
const target = e.target;
if (!target.classList.contains("card")) return;
draggedCard = target;
draggedCard.classList.add("dragging");
// 指に追従させるカードを作成
dragPreview = draggedCard.cloneNode(true);
dragPreview.classList.add("drag-preview");
document.body.appendChild(dragPreview);
};
const handleTouchMove = (e) => {
// 画面にタッチしている指を動かしたときのイベント
if (draggedCard == null) return;
e.preventDefault(); // ブラウザ既定の処理を禁止
const touch = e.touches[0]; // 最初のタッチポイント
// 指に追従させるカードを指に追従させる
dragPreview.style.left = `${touch.clientX}px`;
dragPreview.style.top = `${touch.clientY}px`;
dragPreview.style.display = "block";
// 全ボックスの box-over クラスを除去し改めて指の下にあるボックスだけに box-over クラスを付与
document.querySelectorAll(".box").forEach(box => {box.classList.remove("box-over");});
const element = document.elementFromPoint(touch.clientX, touch.clientY);
if (isDroppableBox(element)) element.classList.add("box-over");
};
const handleTouchEnd = (e) => {
// 画面にタッチしている指を放したときのイベント
e.preventDefault(); // ブラウザ既定の処理を禁止
if (draggedCard == null) return;
const touch = e.changedTouches[0]; // タッチ終了位置
const element = document.elementFromPoint(touch.clientX, touch.clientY);
if (isDroppableBox(element)) element.appendChild(draggedCard);
// ドラッグ中のカードと指に追従させるカードをリセット
draggedCard.classList.remove("dragging");
document.querySelectorAll(".box").forEach(box => {box.classList.remove("box-over");});
draggedCard = null;
if (dragPreview) dragPreview.remove();
dragPreview = null;
};
if ("ontouchstart" in window) {
// スマートフォンであったらページにタッチイベントを登録
document.addEventListener("touchstart", handleTouchStart, { passive: false });
document.addEventListener("touchmove", handleTouchMove, { passive: false });
document.addEventListener("touchend", handleTouchEnd, false);
}
let body_lock = false;
const lock = document.getElementById('lock');
const e_html = document.getElementsByTagName('html');
const e_body = document.getElementsByTagName('body');
function toggle_lock(){
if(!body_lock){
lock.innerHTML="画面ロック:ON";
e_html[0].style.overflow = 'hidden';
e_body[0].style.overflow = 'hidden';
body_lock = true;
}else{
lock.innerHTML="画面ロック:OFF";
e_html[0].style.overflow = 'auto';
e_body[0].style.overflow = 'auto';
body_lock = false;
}
}
lock.addEventListener('touchstart', function(e){
toggle_lock();
}, false);
lock.addEventListener('click', function(e){
toggle_lock();
}, false);
const check_button = document.getElementById('check');
function check(){
if((s5.parentNode.id == "b1"||s8.parentNode.id == "b1"||s13.parentNode.id == "b1"||s20.parentNode.id == "b1"||s21.parentNode.id == "b1"||s55.parentNode.id == "b1")&& //か
s50.parentNode.id == "b2"&& //れ
s3.parentNode.id == "b3"&& //ー
(s37.parentNode.id == "b4"||s40.parentNode.id == "b4")&& //ら
(s10.parentNode.id == "b5"||s15.parentNode.id == "b5"||s28.parentNode.id == "b5"||s31.parentNode.id == "b5"||s48.parentNode.id == "b5"||s49.parentNode.id == "b5")&& //い
(s2.parentNode.id == "b6"||s26.parentNode.id == "b6")){ //す
result.innerHTML="☆☆ 正解! ☆☆";
}else{
result.innerHTML="不正解";
}
}
// スマホ対応: タッチイベントとクリックイベントを両方設定
check_button.addEventListener('touchstart', function(event) {
check();
}, false);
check_button.addEventListener('click', function(event) {
check();
}, false);
簡単に言うと
containerのセルに移動できる要素cardをドラッグアンドドロップAPIをスマホ・PC両方で動くようにして移動できるように設定
↓
判定は決まったcontainerセルに決まったcardが入っているかだけ判定
という感じになっています。
コードは
1)index.html
2)sp.html
3)p5_9th.html
の順に簡単になっていますので1から読んでいけば理解しやすいかと。
スマホ対応については下記の記事に大変お世話になりました。
【HTML】要素を置き場から置き場にドラッグ&ドロップする (PC+スマートフォン両対応)
https://qiita.com/Cookieword_box26/items/15cf863ddba02701aa6f