要素内のスクロール量を取得する話になる。
用途
例えば申込みフォーム。
各種項目の設置は当然として、合わせて規約も置いておく。必須項目を入力し、規約を承諾した上で申し込んでもらう。
だのに規約を無視して申し込んであとから揉めるってケースはある。仕方ない。あっても読まないくせに文句を垂れるやつは一定数いる。リテラシーの低いやつも相手にしなきゃいけないというのが、オープンなサービスの辛いところ。
サービス側はリスクヘッジのために規約を読ませたい。規約を読んだってことを申込者に能動的に発信してもらいたい。そのためにどうするかっていえば、大体は以下の通り。
- 「承諾する」チェックボックスの設置
- 「承諾した上で申し込む」申込みボタンに表記
- 規約を最後まで読まないと申込みボタンを押させない
上2つはやろうと思えば誰でも組めるので、めんどくさい3番目をやるのが今回の話。
やっていく
まずここを読む
次にここを読む
組み合わせたら完成。
作例
HTML・CSSをこんな感じにしておいて
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>
<script src="scripts.js"></script>
<style>
body{
background: #f5f5f5;
}
.wrap{
width: 100%;
max-width: 320px;
margin: 0 auto;
background: #fff;
}
ul{
margin: 0 auto;
padding: 20px;
list-style: none;
}
ul li + li{
margin-top: 20px;
}
textarea{
width: 100%;
height: 100px;
box-sizing: border-box;
resize: vertical;
}
button{
width: 100%;
height: 40px;
}
</style>
</head>
<body>
<div class="wrap">
<p>最後まで読んでね</p>
<ul>
<li>
<textarea readonly>
中身をいっぱい書く
</textarea>
</li>
<li><button type="submit" disabled>申し込む</button></li>
</ul>
</div>
</body>
</html>
jQueryでこうする
$(function(){
var scroll_y = $('textarea').get(0).scrollHeight;
var scroll_ny = $('textarea').get(0).offsetHeight;
$('textarea').scroll(function(){
var scroll_p = $('textarea').scrollTop();
var scroll = scroll_p + scroll_ny;
if(scroll_y < scroll){
$('button').prop('disabled', false);
}else{
$('button').prop('disabled', true);
}
});
})
一番下までスクロールしたときだけbuttonのdisabledが解ける。一番下までスクロールしたあとに上に戻ったらまたdisabledになる。一旦解除したら解除しっぱなしにしたければ、if内のelseを削除。
動作はこうなる。
まとめ
色々調べて構築する感じかなって思ったら正解が書かれてる記事があってめっちゃ早く済んでしまった。よかったです。
別案
textareaとかの枠内じゃなくて、ページ自体をスクロールするって手もある。
読ませたいんだからページ自体が長くなろうが関係ないだろう、むしろ装飾とかが楽になるし、みたいな。それはそれでいいんだけど、そうすると尚の事スクロールに注力して読まないんじゃないの?とか、いやどっちでも変わらんだろうとか。これだとsubmit位置までスクロールする=読んでるってことで、disabledのギミックは必要ないんじゃない?ってなって組むのは楽。でもそれだとね、どうなんですか。
アングラ寄りなサイトだときちんと読まないとsubmitの場所がわからない、みたいな感じにしてたりして、個人的にはそれも好きなんだけど。餡庫とかみたいな。まっとうなサービスでのリスクヘッジに向くのか?というね。難しい。
おまけ
disabled状態をcssで装飾することは可能。
button{ /* 通常 */
}
button:disabled{ /* disabled時 */
}
iPhoneとかだと色が一気に変わるので目が痛いから変えたいとか、chromeだと対して変わらないから分かりにくくて変えたいとか、そういう場合に。
追記:改訂版
こう。
$(function(){
let trigger = $('textarea');
let target = $('button');
target.prop('disabled', true);
trigger.on('scroll',function(){
let scroll_y = trigger.get(0).scrollHeight;
let scroll_ny = trigger.get(0).offsetHeight;
let scroll_p = trigger.scrollTop();
let scroll = scroll_p + scroll_ny;
if(scroll_y <= scroll){
target.prop('disabled', false);
}
});
})
まず対象を変数化。
ベタ打ちは使い勝手が悪いし、処理が遅くなるとかどっかで読んだ気がする。
最初に書いたやつはページ読み込み時にtextareaの各種高さを取得していた。
textareaのサイズが変更された場合を考慮してなかったんで、幅広にしたらtextareaの中身の高さが減ってdisabled解除の発火ができなくなる障害があった。
「textareaをスクロールしたタイミングで取得」に変更。
ついでにdisabledをjsで追加するようにした。
これによりContactForm7とかの、「disabledを設置できない環境」にも対応。
発火の条件分岐については「<」でなく「<=」に変更。上手いこと「=」になっちゃったおかげで発火しないことがあった。危ない。
更には、最後まで読んだら以降は戻ってもdisabledにならないようにした。
一旦最後まで行ったんだから、もうそれでいいじゃんっていう話。
コメント