Edited at

アドレスバーのURLを書き換える

googleのエイプリルフールネタの「Google 日本語入力 ピロピロバージョン」を見ていたら、

https://www.google.co.jp/ime/___o/

画面をスクロールすると、アドレスバーのURLも下記のように画面更新しないでピロピロと変わっていた。

https://www.google.co.jp/ime/___o/


https://www.google.co.jp/ime/________o/

https://www.google.co.jp/ime/__________/

調べてみたら、Javascriptのhistory.replaceState()history.pushState()を使うと、アドレスバーのURLを書き換えられるみたい。


// 履歴エントリの置き換え
// http://exapmple.com/xxx/123 → http://example.com/xxx/aiueo
history.replaceState('','','aiueo');

// http://exapmple.com/xxx/123 → http://example.com/aiueo
history.replaceState('','','/aiueo');

// 履歴エントリの追加
history.pushState('','','aiueo');

ブラウザの履歴を操作する - ウェブデベロッパーガイド | MDN

https://msdn.microsoft.com/library/hh920758(v=vs.85).aspx


実際に動かしてみる

試しに、ブラウザのコンソールに打ち込むと、秒表示&「o」を秒数分出力を1秒ごとに行う、スクリプトを書いてみた。

setInterval(function(){window.history.replaceState('','', (new Date().getSeconds()) + ''+ Array((new Date().getSeconds()+1)).join('o'))},1000);

ブラウザのコンソールに上を打ち込むと、アドレスバーのURLが下記のように1秒ごとに書き換えられる。Macだと🍣絵文字が使えるので色々出来そう。

http://example.com/21ooooooooooooooooooooo

ブラウザのアドレスバーに、下記のjavascript:〜の形式で打ちこんででもいける。

javascript:setInterval(function(){window.history.replaceState('','', (new Date().getSeconds()) + ''+ Array((new Date().getSeconds()+1)).join('🍺'))},1000);


Hashで書いてみる

このURLの置き換えのreplaceState、pushStateはHTML5から対応だけれども、

hash(#)の置き換えであれば、HTML5以前のブラウザでもlocation.hashを使って置き換えられる。

例えば、以前のTwitterのURLはtwitter.com/#!/xxxxのようになっていて、画面遷移無しで#!以降が書き換えられていた。

jQueryで画面遷移のないサイトを作ろうとしたときのちょっとしたメモ | webOpixel

XMLの第一人者Tim Bray氏「URLに#!入れるな」:濃縮還元オレンジニュース|gihyo.jp … 技術評論社

/* http://example.com/#aiueo の#以降を書き換える。*/

location.hash='aiueo';

つまり、location.hashを使っても同じようなことが出来る。

setInterval(function(){location.hash=(new Date().getSeconds()) + ''+ Array((new Date().getSeconds()+1)).join('o')},1000);


どういったところで使われるのか

どうしてこういったURLの書き換えできる機能があるのかというと

パラメータ付きURLのパラメータを消すために使ったりするようだ。

キャンペーン用トラッキングパラメータ付きURLを、HTML5のreplaceStateで美しくスッキリさせてみた | 編集長ブログ―安田英久 | Web担当者Forum

あとは、Googleの画像検索のような、スクロールで自動でページを継ぎ足しするで、ページ番号をURLに保持させたいときとかな。

AutoPatchWork でページ継ぎ足し時に replaceState で URL を書き換える - #生存戦略 、それは - subtech

あと、以前のTwitterの様に

画面遷移しないけれども、ブックマークやSEOの問題で同一URLにはしたくないページ対策としてあったhashbang(#!)  例:twitter.com/#!/xxxx に替わるものではと思う。

Twitterは2012年にhashbang(#!)はパフォーマンスの問題で廃止しているみたいだけれども。

Twitter.comの読み込みが高速化 5分の1に短縮 - ITmedia NEWS

GitHubのような画面のリロードなしでコンテンツが変わってURLも変わるページを実現するプラグインとして、「PJAX」があるけれどこれはpushStateを使っているっぽい。

http://falsandtru.github.io/jquery-pjax/


悪用されたりしないか

悪用されないか考えてみたけれど、限られた条件じゃないと難しい気がする。

ドメインまでは書き換えられないので、別サイトに見せかけることは難しい。

http://example.com/~username/ のようにサブディレクトリで異なるサイトの場合は書き換えられる恐れがあるが、Javascriptが個別に設定できることが前提。

そもそもJavascriptが使えるのならば、URL書き換えどころか、もっと色々出来そう。

jsfiddleみたいな、オンラインでJavascriptで実行できるところは、サンドボックスになっていてreplaceStateの影響は受けないし、

個人WEBサイトを作るようなところはサブドメインで分かれていることが多いので、これもreplaceStateの影響は受けない。

アクセス履歴が書き換えられるが、別ドメインまで書き換えられないので、範囲は限定される。

アクセス履歴の表示は、現在はdocument.titleの内容が表示されているので、

document.titleの書き換えができれば


history.pushState('','','1');
document.title = "";
history.pushState('','','2');
document.title = "";
history.pushState('','','3');
document.title = "";
history.pushState('','','4');
document.title = "";
history.pushState('','','5');
document.title = "";

という風に、タイトルは変えることはできるぐらいだが、

自分のサイトのtitleを可変で変えているだけなので、悪用とまではいかない。

(※サンプル画像はブラウザのコンソールからJavascriptを入力しているので、Qiitaのアイコンが出ているだけ)


その他の使い道

googleのエイプリルフールネタにもあったように、URLを書き換える目的以外に、

アドレスバーに何か表示するエリアとしての使い道があることが分かった。

なんか他に使い道がないか。ニュースを流したり、ゲーム的な物をやったりとか。

ズンドコ的なもの


zundoko.js


z='ズン';d='ドコ';s='';a=[];
setInterval(function () {
if (a.join('') === z+z+z+z+d) {
s='キ・ヨ・シ!';
console.log(s);
window.history.replaceState('', '',s);
s='';a=[];
} else {
a.shift();
s = Math.random() < 0.5 ? z : d;
a[4] = s;
console.log(s);
window.history.replaceState('', '',a.join(''));
}
}, 500);


RSSフィードのタイトルを2秒おきに表示(JQueryを使う場合)

// jqueryが読み込まれていない場合は、下記を使って読み込ませる。
/*
document.body.appendChild(function(){
src="https://code.jquery.com/jquery-latest.min.js";
sc = document.createElement("script");
sc.type="text/javascript";
sc.src=src;
sc.onload = function() {};
return sc;
}()
);
*/

// MSNのRSSフィードを読み込む
$.get("https://rss.msn.com/ja-jp/", function(data) {
$(data).find("item").each(function(idx, item) {
// タイトル部分を取得
var title = $(item).find("title").text();
// 2秒おきに表示を切り替える
setTimeout(function(){location.hash = title;}, 2000 * idx);
});
});

RSSフィードのタイトルを2秒おきに表示(JQueryを使わないでfetch apiを使う場合)


fetch("https://rss.msn.com/ja-jp/")
.then(response => response.text())
.then(text => (new DOMParser()).parseFromString(text, "application/xml"))
.then(doc => doc.querySelectorAll("item"))
.then(items => {
items.forEach((item, idx) => {
setTimeout(() => {
const title = item.querySelector("title").textContent;
location.hash = title;
}, 2000 * idx)
})
})