⛷️
TS/CSSで行をスライドさせる削除機能
はじめに
Reactで作ったWebアプリの日時別データ登録機能で、行削除を実装した時の記録です。
最終的な見た目
こんな感じの行に対し、
右からにゅっと削除ボタンを出すようにしました。
最初の実装と問題点
最初はiPhone標準機能にあるような、左に向かってスライドすると削除ボタンが現れる、という仕様にしようとしていました。
以下のように overflow:hidden
した親コンポーネントの中で、データごと左にずれる感じです。
スクロールし終わった位置をもとに、左端か右端に自動で吸い寄せられるようにしました。
(動画がなくてわかりづらくすみません。iPhone標準でよくある感じです。)
実装は、TSで以下のように行の左端と右端を scrollIntoView
で表示させるようにしました。
page.tsx
// スクロールする行の親要素
const componentRef = useRef<HTMLDivElement>(null);
// スクロールする行
const contentRef = useRef<HTMLDivElement>(null);
// 行左側のデータ部分
const dataRef = useRef<HTMLDivElement>(null);
// 行右側の削除ボタン部分
const deleteRef = useRef<HTMLDivElement>(null);
const handleScroll = () => {
// 横スクロールし終わった時点の左端の位置を取得
const left = contentRef.current?.getBoundingClientRect().left;
// 一定の距離までスクロールされたら
if (left !== undefined && left < 14) {
// アニメーション付きで、行の右端まで自動スクロール(削除ボタン表示)
deleteRef.current?.scrollIntoView({ behavior: "smooth", inline: "end" });
// スクロール距離が足りなかったら
} else {
// アニメーション付きで、行の左端まで自動スクロールで戻す(削除ボタン非表示)
dataRef.current?.scrollIntoView({ behavior: "smooth", inline: "start" });
}
};
// スクロールが終了したことを検知
useEffect(() => {
const element = componentRef.current;
if (element) {
element.addEventListener("scrollend", handleScroll);
return () => {
element.removeEventListener("scrollend", handleScroll);
};
}
}, []);
PCとAndroidのChromeではうまくいきましたが、iPhoneのChromeでは自動スクロールが動いてくれず、スクロールした位置でそのまま止まってしまいました。
iPhoneでも動くように修正してみようかと思いましたが、そもそもiPhoneユーザー以外がスライドで削除ボタンを表示することに慣れていないので、この機能自体違和感を持ってしまうのではと思い、他の仕様に変更することに。
最終的な実装
結局、表の上に配置した「編集」というボタンを押したら、すべての行で一斉に削除ボタンが表示される、という仕様にしました。
一斉に表示された時のイメージ図↓
削除ボタン表示のアニメーションは、CSSで以下のように実装しました。
page.module.css
// はみ出た削除ボタン部分を隠している親要素
.component {
width: 100%;
height: 42px;
position: relative;
overflow: hidden;
border-radius: 5px;
}
// 削除ボタンを含む子要素
// widthで、100%はデータ行部分、70pxは削除ボタン部分
// transitionで、widthが変化した時にアニメーションさせている
.content {
display: flex;
flex-direction: row;
font-weight: 700;
background-color: white;
width: calc(100% + 70px);
height: 100%;
position: absolute;
transition: width 0.3s;
}
// 編集ボタン押下時、widthを削除ボタン含めて親要素の範囲内におさめる
.showDelete {
width: 100%;
}
おわりに
結局、iPhoneでだけ scrollIntoView
が効かなかった原因がわからずじまいです。
scrollIntoView
が効いていないのか、他に問題があったのか・・・。
Discussion