Hit-a-Hintブックマークレット(さらに)改良版


createText 関数に誤りがあったのを修正。

この前の記事についたコメント

id:oryzan
どうも。
以前edvakfさんがコメントされていた、
「ShiftとCtrlの押されているときはヒントを消さないようにすればOperaの機能で新しいページをどこに開くか決定できそうな気がしたり(想像)。」

についてですが、キー設定のところで単純に以下のようにしたらうまく動作しました。
var keycodemapping = {’13’:’Enter’,’27’:’Esc’,’8’:’Bkspc’,’44’:’,’,’16’:’’,’17’:’’}

これで;キー版とfキー版で分ける必要は無くなり、うまく改良できるかと思います。
(インクリメントでの自動動作はあきらめなければですが)

を加味した修正。

キーコードの16と17はそれぞれCtrl(MacならCommand)とShiftになるので、押したキーがそれらだった場合にはヒントを消さずに何も実行しないようにすることができる。oryzanさんありがとう。

javascript:(function(){var hintkeys='asdfghjkl';function createText(num){var ret='';var l=hintkeys.length;var iter=0;var n;while(num>=0){n=num;num-=l*Math.pow(l,iter);iter++;}for(var i=0;i<iter;i++){r=n%l;n=Math.floor(n/l);ret=hintkeys.charAt(r)+ret;}return ret;}function retrieveNumber(w){var ret=0;var wlen=w.length;for(var i=0;i<wlen;i++){var fix=(i==0)?0:1;var t=w.charAt(wlen-i-1);ret+=(hintkeys.indexOf(t)+fix)*Math.pow(hintkeys.length,i);}return ret;}var bgcolor = '#FF0';var bghighlight = '#0F0';var color = '#000';var hintlist = new Array();var hintedlinks = new Array();var map = new Array();var choices = new Array();var mapindex = 0;var choice = '';var keycodemapping = {'13':'Enter','27':'Esc','8':'Bkspc','44':',','16':'','17':''};for(var i=0;i<hintkeys.length;i++){var ithkey=hintkeys[i];keycodemapping[ithkey.charCodeAt(0)]=ithkey;}var originalTitle = document.title;function drawHints(){document.addEventListener('keypress',interpretKeyStroke,true);document.title+=' - ';var allLinks = document.getElementsByTagName('a');var viewportStart = window.pageYOffset - 5;var viewportEnd = viewportStart + window.innerHeight + 10;for (var i = 0;i<allLinks.length;i++){linkYcoord = getAbsoluteY(allLinks[i]);if(linkYcoord > viewportStart && linkYcoord < viewportEnd && allLinks[i].href != '' && allLinks[i].href.indexOf('javascript:void(0)') && allLinks[i].href!=location.href.replace(location.hash,'')+'#'){hintedlinks.push(allLinks[i]);}}for (var i = 0;i<hintedlinks.length;i++){var hint = document.createElement('span');hintlist.push(hint);hint.style.backgroundColor=bgcolor;hint.style.color=color;hint.style.position='absolute';hint.innerHTML = createText(mapindex);map[createText(mapindex)]=hintedlinks[i].href;mapindex++;hintedlinks[i].appendChild(hint,hintedlinks[i]);}}function getAbsoluteY(element){var y = 0;while (element) {y += element.offsetTop;element = element.offsetParent;}return y;}function removeHints(){for (var i=0;i<hintedlinks.length;i++){hintedlinks[i].removeChild(hintlist[i],hintedlinks[i]);}choice='';document.title=originalTitle;document.removeEventListener('keypress',interpretKeyStroke,true);delete map;delete hintlist;delete hintedlinks;delete choices;}function interpretKeyStroke(e){e.preventDefault();var key=keycodemapping[(typeof event!='undefined')?window.event.keyCode:e.keyCode];if(key=='Enter'){choices.push(choice);var windowname=(choices.length==1 && !e.ctrlKey)?'_top':'_blank';for (var i=0;i<choices.length;i++){if(map[choices[i]]!=undefined){window.open(map[choices[i]],windowname);}}removeHints();}else if(key==','){if(choice!=''){choices.push(choice);hintlist[retrieveNumber(choice)].style.backgroundColor=bghighlight;choice = '';document.title+=',';}}else if(key=='Esc'){removeHints();}else if(key=='Bkspc'){if(choices.length){if(choice!=''){choice='';}else{hintlist[retrieveNumber(choices.pop())].style.backgroundColor=bgcolor;document.title=originalTitle+' - '+choices.join(',');}}}else if(key == undefined){removeHints();}else{choice+=key;var choicestring = (choices.length) ? choices.join(',')+','+choice : choice;document.title=originalTitle+' - '+choicestring;}}drawHints();})();

その他に、選択したヒントのリンクを開くところがこうなっていた↓のを

for (var i=0;i<choices.length;i++) {if(map[choices[i]]!=undefined){window.open(map[choices[i]]);}}

このようにした↓

var windowname=(choices.length==1 && !e.ctrlKey)?'_top':'_blank';for (var i=0;i<choices.length;i++){if(map[choices[i]]!=undefined){window.open(map[choices[i]],windowname);}}

これにより、一つしかヒントを選択していないときは、Enterのみなら現在のタブにリンクを開き、Ctrl+Enterで前面の新タブに開き、さらにCtrl+Shift+EnterならOperaの機能で背面の新タブで開くようになった。複数のヒントを選択している場合は、Enterなら前面の新タブに開き、Ctrl+Shift+Enterなら背面の新タブで開く


これとは別に、個人的に引っ掛かっていた問題、href="javascript:void(0)"とかhref="#"なリンクを叩いてしまって開いてガッカリする、ということがある。というわけで、そういうリンクにはそもそもヒントをつけないようにもしてみた。

ただしこの変更は好みが分かれるかもしれないので、これが嫌な人はdrawHints関数の中で、こう↓なっているところを

if(linkYcoord > viewportStart && linkYcoord < viewportEnd && allLinks[i].href != '' && allLinks[i].href.indexOf('javascript:void(0)') && allLinks[i].href!=location.href.replace(location.hash,'')+'#')

このように↓変更すれば前と同じ状態に戻せる。

if(linkYcoord > viewportStart && linkYcoord < viewportEnd && allLinks[i].href != '')

こういう一見無意味なリンクは何故存在するかというと、onclickイベントでJavaScriptを実行させることにより、

  1. 巡回ロボットがリンクの繋がりを特定できないようにする (ほぼ間違いなく広告などへのリンク?)
  2. クリックを起点にページ遷移を行なわずにWeb2.0的な操作をする

という目的があるらしい。

1なら別にヒントを表示しなくてもいいと思うのだが、2の場合はヒントを表示させないのではなく、ヒントを叩いた時にonclickイベントを発生させたほうが良いように思う。そういうヒントを複数選択した場合にどれを実行させるかなどいろいろややこしくなってくるので、現在のところは保留で。空間ナビゲーションを使うこともできるのだし。

今のところ頭にあるのは、選択ヒントが一つだけのときはそういうリンクへのヒントの色を変えて、そういうヒントを一つだけ選択してエンターを押したときだけonclickイベントを発生させるようにするということなのだが、需要があるのかどうか。(ブックマークレットなんか機能が多すぎないほうがいいという自分の考えにも反する)

良い案があればコメントしてください。