2017.03.21
要素を一定の時間マウスオーバー(ホバー)したら処理を走らせたい話
JavaScriptの話
2017.03.19
グローバルナビゲーションにドロップダウンリストをつけてマウスオーバーしたらピロンと現れる、という機能をCSSでつけたみました。が、あまりに反応が瞬時すぎて次のようなことに困ってしまいました。
- ちょっとでもマウスが乗るとリストが現れてしまうので、見た目がちらちらする。割とうざったい。
- ちょとでもマウスが外れるとリストが消えてしまうのでストレスになる。
もっとユーザー大雑把に操作させてくれてもいいじゃない!ということで、大雑把な操作に耐えられるよう、jQueryを書いてみました。
やりたいこと
該当要素の出現について
○秒ホバー(マウスオーバー)し続けたら処理を走らせる。○秒経つ前にホバーが外れたら何もしない。今回はCSSで「:hover」と記述していたところをクラス名「hover」に置き換えたいので該当の要素に「addClass(‘hover’)」をかけてあげます。
該当要素の撤去について
○秒マウスアウトし続けたら処理を走らせる。○秒経つ前にもともとマウスオーバーされていた要素に再びマウスオーバーされたら、何もしない。今回は先ほどつけられたクラス名を外したいので「removeClass(‘hover’)」をかけてあげます。
結論コード
結論としてはこんなコードになりました。
デモはこちら
鬼みたいに長いんですけどHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
<ul class="menulist"> <li> <a href="#">第一階層メニュー</a> <ul class="second"> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> </ul> </li> <li> <a href="#">第一階層メニュー</a> <ul class="second"> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> </ul> </li> <li> <a href="#">第一階層メニュー</a> <ul class="second"> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> </ul> </li> <li> <a href="#">第一階層メニュー</a> <ul class="second"> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> </ul> </li> <li> <a href="#">第一階層メニュー</a> <ul class="second"> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> <li><a href="#">第二階層メニュー</a> <ul class="third"> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> <li><a href="#">第三階層メニュー</a></li> </ul> </li> </ul> </li> </ul> |
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
ul.menulist { width: 960px; min-height: 400px; margin-left: auto; margin-right: auto; font-size: 0; text-align: center; } ul.menulist > li { width: 20%; display: inline-block; position: relative; box-sizing: border-box; font-size: 16px; vertical-align: top; } ul.menulist > li:after { content: ""; display: block; margin-left: -5px; margin-top: -10px; position: absolute; left: 50%; top: 3em; border: 5px solid transparent; border-top-color: #fff; } ul.menulist li a { display: block; padding: 1em; color: #fff; border: 1px solid #ffffff; background-color: #007602; } ul.menulist > li a:hover { opacity: 0.7; } ul.menulist li ul.second { display: none; } ul.menulist li.hover ul.second { display: block; } ul.menulist li ul.second li { position: relative; } ul.menulist li ul.second > li:after { content: ""; display: block; margin-left: -15px; margin-top: -5px; position: absolute; left: 100%; top: 50%; border: 5px solid transparent; border-left-color: #fff; } ul.menulist li:last-child ul.second > li:after { margin-left: auto; margin-right: -15px; left: auto; right: 100%; border-left-color: transparent; border-right-color: #fff; } ul.menulist li ul.second li a { background-color: #1EB513; } ul.menulist li ul.second li ul.third { display: none; width: 100%; position: absolute; top: 0; left: 100%; } ul.menulist li:last-child ul.second li ul.third { left: auto; right: 100%; } ul.menulist li ul.second li.hover ul.third { display: block; } ul.menulist li ul.second li ul.third li a { background-color: #48D82B; } |
jQuery
jQuery自体の読み込みは割愛します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
function menuShowDelay (element, delayTime) { var sethover; var setleave; var setnexthover; var targetOn; var targetOff; var nowActive = -1; var hoverClass = 'hover'; var manuElement = element; var hoverTime = delayTime; manuElement.on({ 'mouseenter': function(){ targetOn = $(this); if(nowActive === -1){ sethover = setTimeout(function(){ targetOn.addClass(hoverClass); nowActive = manuElement.index(targetOn); }, hoverTime); } else { if(targetOn.hasClass(hoverClass)){ clearTimeout(setleave); } else { setnexthover = setTimeout( function(){ manuElement.removeClass(hoverClass); targetOn.addClass(hoverClass); nowActive = manuElement.index(targetOn); }, hoverTime); } } }, 'mouseleave': function(){ targetOff = $(this); clearTimeout(sethover); function mouseIsOverWorkaround(what){ var temp = $(what).parent().find(":hover"); return temp.length == 1 && temp[0] == what; } var parent= targetOff; if(mouseIsOverWorkaround(parent[0])){ if(targetOff.hasClass(hoverClass)){ clearTimeout(setnexthover); } } else { setleave = setTimeout(function(){ targetOff.removeClass(hoverClass); nowActive = -1; }, hoverTime); } } }); } $(function(){ menuShowDelay($('ul.menulist > li'), 250); menuShowDelay($('ul.second > li'), 250); }); |
正解は「clearTimeout()」。「delay()」「setTimeout()」は遅れるだけ。
「処理 遅らせる」などど検索すると「delay()」「setTimeout()」などが出てきますが、これだけだと処理を始めるまでの時間が遅れるだけで実際に処理はされます。250ミリ秒遅らせたとして、ちょろっとマウスが通過すると250ミリ秒遅れてリストがちょろっと出てすぐ消える、という具合なのでこれではなんだか奇妙です。
前述の結論コードの中でポイントになるのは「clearTimeout()」の部分です。「setTimeout()」と一緒に使い、「setTimeout()」のタイマーを解除します。
これを利用して要素の出現についてだけ記述するとこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 |
$(function(){ var menuItem = $('ul.menu li dl dt'); menuItem.next().hide(); menuItem.mouseenter(function(){ var that = this; hoge = setTimeout(function(){ $(that).next().show(); }, 1000); }).mouseleave(function(){ clearTimeout(hoge); }); }); |
第一階層メニューにマウスが乗ったら250ミリ秒遅れてそこにぶら下がる第二階層メニューが現れる。この一連の動きに「hoge」というキーネームを付けておきます。マウスが乗ってから250ミリ秒経つ前にマウスが外れたら「clearTimeout()」が働き、キーネーム「hoge」の起爆までのタイマーを解除します。
要素の撤去についてはこの逆を行います。すると結論コードのようになります。
かくしてめでたく意図した通りのドロップダウンリストが作れましたとさ。よかったね!
この記事へのコメント
コメントはまだありません。