window.location
とほとんど同じように機能する小さなURLパーサーですが、操作中にブラウザーページをリロードしません。
同時に、JavaScriptのゲッターとセッターに関するいくつかの言葉を話します。
UPD1:労働者の要求に応じて、例を挙げます。
// URL = 'http://my.site.com/somepath/'
var u = new URL( 'relative/path/index.html' )
u.href // my.site.com/somepath/relative/path/index.html
u.href = '/absolute/path.php?a=8#some-hash'
u.href // my.site.com/absolute/path.php?a=8#some-hash
u.hash // #some-hash
u.protocol = 'https:'
u.href // my.site.com/absolute/path.php?a=8#some-hash
u.host = 'another.site.com:8080'
u.href // another.site.com:8080/absolute/path.php?a=8#some-hash
u.port // 8080
// ,
* This source code was highlighted with Source Code Highlighter .
FF3 +(おそらく2+で試していない)とIE6 + (これが私のノウハウ:-))で動作します。
この記事では、完全にクロスブラウザーの実装についても分析していますが、使用中は少し面倒です。
// URL = 'http://my.site.com/somepath/'
var u = new URL( 'relative/path/index.html' )
u.href() // my.site.com/somepath/relative/path/index.html
u.href( '/absolute/path.php?a=8#some-hash' )
u.href() // my.site.com/absolute/path.php?a=8#some-hash
// ..
* This source code was highlighted with Source Code Highlighter .
はい、リスティングの全文を掲載します。申し訳ありませんが、ご参加ください。
UPD2:ライブラリの目標を簡単に説明します。
このツールは、実際のニーズから正確に生まれました。
また、TinyMCEなどの大規模なJSプロジェクトで、この目的のいくつかの職人的開発を見てきました。 RTEでは、多くの場合、リソースリンクを扱います。 また、これらのリンクはリアルタイムで処理する必要があります。
具体的には、現在のURLを解析し、検索で新しいパラメーターを変更/追加してから、リダイレクトを行う必要がありました。
あなたはもっと思いつくことができます。
問題
実際、問題は何ですか? 問題は次のとおりです。
-
window.location
オブジェクトを使用することはできません。 わずかな変更で現在のページをリロードします - コンストラクター
Location
介してこのようなオブジェクトをもう1つ作成することはできません-atat! ブラウザのドライバーによって禁止されています! - オブジェクト自体の動作はかなり重要です。
- さて、私は既製の実装を見つけられませんでした:)
私は行動の非自明性に言及しました。 ここにあります:
URLの一部を変更すると、他の部分も更新する必要があります。
別れ
本質的に、window.locationの
window.location
を作成するので、そこから表記をドラッグします。 例を見てみましょう:
コメントなし:)
好むと好まざるとにかかわらず、RegExpは不可欠です
もちろん、主な作業は正規表現によって行われます。
var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;
* This source code was highlighted with Source Code Highlighter .
より詳細に:
var pattern =
// Match #0. URL (#0 - HREF, window.location).
// , #0 == "https://example.com:8080/some/path/index.html?p=1&q=2&r=3#some-hash"
"^" +
// Match #1 & #2. SCHEME (#1 - PROTOCOL, window.location).
// , #1 == "https:", #2 == "https"
"(([^:/\\?#]+):)?" +
// Match #3-#6. AUTHORITY (#4 = HOST, #5 = HOSTNAME #6 = PORT, window.location)
// , #3 == "//example.com:8080", #4 == "example.com:8080", #5 == "example.com", #6 == "8080"
"(" +
"//(([^:/\\?#]*)(?::([^/\\?#]*))?)" +
")?" +
// Match #7. PATH (#7 = PATHNAME, window.location).
// , #7 == "/some/path/index.html"
"([^\\?#]*)" +
// Match #8 & #9. QUERY (#8 = SEARCH, window.location).
// , #8 == "?p=1&q=2&r=3", #9 == "p=1&q=2&r=3"
"(\\?([^#]*))?" +
// Match #10 & #11. FRAGMENT (#10 = HASH, window.location).
// , #10 == "#some-hash", #11 == "some-hash"
"(#(.*))?" + "$" ;
* This source code was highlighted with Source Code Highlighter .
ご想像のとおり、このRegExpはJavaScriptだけでなく、他の何百もの言語でも機能します。 健康のために使用してください! ;)
試行番号1
function URL(url) {
url = url || "" ;
this .parse(url);
}
URL.prototype = {
// this.href, this.parse()
href: "" ,
// - , this.update()
protocol: "" ,
host: "" ,
hostname: "" ,
port: "" ,
pathname: "" ,
search: "" ,
hash: "" ,
parse: function (url) {
url = url || this .href;
var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;
var rx = new RegExp(pattern);
var parts = rx.exec(url);
this .href = parts[0] || "" ;
this .protocol = parts[1] || "" ;
this .host = parts[4] || "" ;
this .hostname = parts[5] || "" ;
this .port = parts[6] || "" ;
this .pathname = parts[7] || "/" ;
this .search = parts[8] || "" ;
this .hash = parts[10] || "" ;
this .update();
},
update: function () {
// protocol - ,
if (! this .protocol)
this .protocol = window.location.protocol;
// relative pathname/URL - relative, ""
this .pathname = this .pathname.replace(/^\s*/g, '' );
if (! this .host && this .pathname && !/^\ //.test(this.pathname)) {
// , . .
var _p = window.location.pathname.split( '/' );
_p[_p.length - 1] = this .pathname;
this .pathname = _p.join( '/' );
};
// hostname - ,
if (! this .hostname)
this .hostname = window.location.hostname;
this .host = this .hostname + (( "" + this .port) ? ":" + this .port : "" );
this .href = this .protocol + '//' + this .host + this .pathname + this .search + this .hash;
},
/**
* window.location. URL.
*/
assign: function (url) {
this .parse(url);
window.location.assign( this .href);
},
/**
* window.location. URL, history
*/
replace: function (url) {
this .parse(url);
window.location.replace( this .href);
}
}
* This source code was highlighted with Source Code Highlighter .
詳細に
- ご覧のとおり、
window.location
href, port, hash
などには通常の属性があります。 -
window.location
メソッドassign(...), replace(...)
よく知られているassign(...), replace(...)
-
parse(...)
メソッドは主な仕事をします-URLをその構成部分に解析します。 - そして、
update(...)
メソッド-いずれかのパーツが変更された場合、すべてのパーツを更新します。
すべては問題ありませんが、URLの一部(ポートなど
parse(...)
を変更した後は、 常に
update(...)
と
parse(...)
呼び出す必要があります。 これはひどいです。 結局、ユーザーはこれを行うのを忘れることができ、それからすべてがtara-tararaに飛びます。
残念ながら、この実装は避けられません。 しかし、あなたはすべてを異なって行うことができます:)
試行番号2
そして今、私は許容可能なオプションを提供します。 ゲッターとセッターが必要です。 最も明白な方法は、パラメーターごとにメソッドを作成することです(
getProtocol() & setProtocol(newProtocol)
。 しかし、私はこのアプローチがかさばるので好きではありません。
もっとJavaScriptの方法でやってみましょう。
protocol(...)
メソッドが1つあり、パラメータなしで呼び出すとゲッターになり、パラメータを1つ使用するとセッターになります。
回路内の実際のデータを非表示にします。
var URL;
// . , .. parseURL updateURL.
( function () {
URL = function (url) {
// , . URL - , .
var href, protocol, host, hostname, port, pathname, search, hash;
// - , .
// Get/set href - set parseURL.call(this),
// .. parseURL URL - this.
this .href = function (val) {
if ( typeof val != "undefined" ) {
href = val;
parseURL.call( this );
}
return href;
}
// Get/set protocol
// set href, set protocol updateURL.call(this), .
this .protocol = function (val) {
if ( typeof val != "undefined" ) {
// - protocol , window.location
if (!val)
val = protocol || window.location.protocol;
protocol = val;
updateURL.call( this );
}
return protocol;
}
// Get/set host
// , host, hostname port - .
// set host.
this .host = function (val) {
if ( typeof val != "undefined" ) {
val = val || '' ;
var v = val.split( ':' );
var h = v[0], p = v[1] || '' ;
host = val;
hostname = h;
port = p;
updateURL.call( this );
}
return host;
}
// Get/set hostname
// host, hostname port.
this .hostname = function (val) {
if ( typeof val != "undefined" ) {
if (!val)
val = hostname || window.location.hostname;
hostname = val;
host = val + (( "" + port) ? ":" + port : "" );
updateURL.call( this );
}
return hostname;
}
// Get/set port
// host, hostname port.
this .port = function (val) {
if ( typeof val != "undefined" ) {
port = val;
host = hostname + (( "" + port) ? ":" + port : "" );
updateURL.call( this );
}
return port;
}
// Get/set pathname
// pathname .
// relative pathname, .. set' pathname,
// '/', .
this .pathname = function (val) {
if ( typeof val != "undefined" ) {
if (val.indexOf( "/" ) != 0) { // relative url
var _p = (pathname || window.location.pathname).split( "/" );
_p[_p.length - 1] = val;
val = _p.join( "/" );
}
pathname = val;
updateURL.call( this );
}
return pathname;
}
// Get/set search
this .search = function (val) {
if ( typeof val != "undefined" ) {
search = val;
}
return search;
}
// Get/set hash
this .hash = function (val) {
if ( typeof val != "undefined" ) {
hash = val;
}
return hash;
}
url = url || "" ;
parseURL.call( this , url);
}
URL.prototype = {
/**
* window.location. URL.
*/
assign: function (url) {
parseURL.call( this , url);
window.location.assign( this .href());
},
/**
* window.location. URL, history
*/
replace: function (url) {
parseURL.call( this , url);
window.location.replace( this .href());
}
}
// , URL .
// - URL.
// , .. .
function parseURL(url) {
if ( this ._innerUse)
return ;
url = url || this .href();
var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;
var rx = new RegExp(pattern);
var parts = rx.exec(url);
// Prevent infinite recursion
this ._innerUse = true ;
this .href(parts[0] || "" );
this .protocol(parts[1] || "" );
//this.host(parts[4] || "");
this .hostname(parts[5] || "" );
this .port(parts[6] || "" );
this .pathname(parts[7] || "/" );
this .search(parts[8] || "" );
this .hash(parts[10] || "" );
delete this ._innerUse;
updateURL.call( this );
}
// , URL .
// - URL.
// , .. .
// , , setter'.
function updateURL() {
if ( this ._innerUse)
return ;
// Prevent infinite recursion
this ._innerUse = true ;
this .href( this .protocol() + '//' + this .host() + this .pathname() + this .search() + this .hash());
delete this ._innerUse;
}
})()
* This source code was highlighted with Source Code Highlighter .
一般に、コードは自己文書化されているため、重要な点のみを説明します。
- プロトタイプは、
parseURL(...)
およびupdateURL(...)
関数でそれぞれ作成された2つのメソッドparse(...)
およびupdate(...)
にparseURL(...)
れました - また、すべてのデータ(
href, port, host
など)はプロトタイプを離れ、コンストラクターによって作成されたクロージャーに収まりました。 ゲッターとセッターを介して作業するようになりました
例
さて、すぐに例に。 結局のところ、主なことは、このことを実際に見ることです。
// URL = 'http://my.site.com/somepath/'
var u = new URL( 'relative/path/index.html' )
u.href() // my.site.com/somepath/relative/path/index.html
u.href( '/absolute/path.php?a=8#some-hash' )
u.href() // my.site.com/absolute/path.php?a=8#some-hash
u.hash() // #some-hash
u.protocol( 'https:' )
u.href() // my.site.com/absolute/path.php?a=8#some-hash
u.host( 'another.site.com:8080' )
u.href() // another.site.com:8080/absolute/path.php?a=8#some-hash
u.port() // 8080
// ,
* This source code was highlighted with Source Code Highlighter .
行くぞ すべてが機能します。
実際、これは機能するバージョンです。 バージョン1.0 finalと呼びます。
それでは、バージョン2.0アルファに移りましょう。または、truゲッターとセッターが登場します。
試行番号3
コードを提供し、興味深い点を検討します。
var URL;
( function () {
var isIE = window.navigator.userAgent.indexOf( 'MSIE' ) != -1;
URL = function (url) {
var data = {href: '' , protocol: '' , host: '' , hostname: '' , port: '' , pathname: '' , search: '' , hash: '' };
var gs = {
getHref: function () {
return data.href;
},
setHref: function (val) {
data.href = val;
parseURL.call( this );
return data.href;
},
getProtocol: function () {
return data.protocol;
},
setProtocol: function (val) {
if (!val)
val = data.protocol || window.location.protocol; // update || init
data.protocol = val;
updateURL.call( this );
return data.protocol;
},
getHost: function () {
return data.host;
},
setHost: function (val) {
val = val || '' ;
var v = val.split( ':' );
var h = v[0], p = v[1] || '' ;
data.host = val;
data.hostname = h;
data.port = p;
updateURL.call( this );
return data.host;
},
getHostname: function () {
return data.hostname;
},
setHostname: function (val) {
if (!val)
val = data.hostname || window.location.hostname; // update || init
data.hostname = val;
data.host = val + (( "" + data.port) ? ":" + data.port : "" );
updateURL.call( this );
return data.hostname;
},
getPort: function () {
return data.port;
},
setPort: function (val) {
data.port = val;
data.host = data.hostname + (( "" + data.port) ? ":" + data.port : "" );
updateURL.call( this );
return data.port;
},
getPathname: function () {
return data.pathname;
},
setPathname: function (val) {
if (val.indexOf( "/" ) != 0) { // relative url
var _p = (data.pathname || window.location.pathname).split( "/" );
_p[_p.length - 1] = val;
val = _p.join( "/" );
}
data.pathname = val;
updateURL.call( this );
return data.pathname;
},
getSearch: function () {
return data.search;
},
setSearch: function (val) {
return data.search = val;
},
getHash: function () {
return data.hash;
},
setHash: function (val) {
return data.hash = val;
}
};
if (isIE) { // IE5.5+
var el= document .createElement( 'div' );
el.style.display= 'none' ;
document .body.appendChild(el);
el.assign = URL.prototype.assign;
el.replace = URL.prototype.replace;
var keys = [ "href" , "protocol" , "host" , "hostname" , "port" , "pathname" , "search" , "hash" ];
el.onpropertychange= function (){
var pn = event .propertyName;
var pv = event .srcElement[ event .propertyName];
if ( this ._holdOnMSIE || pn == '_holdOnMSIE' )
return pv;
this ._holdOnMSIE = true ;
for ( var i = 0, l = keys.length; i < l; i++)
el[keys[i]] = data[keys[i]];
this ._holdOnMSIE = false ;
for ( var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
if (pn == key) {
var sKey = 'set' + key.substr(0, 1).toUpperCase() + key.substr(1);
return gs[sKey].call(el, pv);
}
}
}
url = url || "" ;
parseURL.call(el, url);
return el;
} else if (URL.prototype.__defineSetter__) { // FF
var keys = [ "href" , "protocol" , "host" , "hostname" , "port" , "pathname" , "search" , "hash" ];
for ( var i = 0, l = keys.length; i < l; i++) {
( function (i) {
var key = keys[i];
var gKey = 'get' + key.substr(0, 1).toUpperCase() + key.substr(1);
var sKey = 'set' + key.substr(0, 1).toUpperCase() + key.substr(1);
URL.prototype.__defineGetter__(key, gs[gKey]);
URL.prototype.__defineSetter__(key, gs[sKey]);
})(i);
}
url = url || "" ;
parseURL.call( this , url);
}
}
URL.prototype = {
assign: function (url) {
parseURL.call( this , url);
window.location.assign( this .href);
},
replace: function (url) {
parseURL.call( this , url);
window.location.replace( this .href);
}
}
function parseURL(url) {
if ( this ._innerUse)
return ;
url = url || this .href;
var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;
var rx = new RegExp(pattern);
var parts = rx.exec(url);
// Prevent infinite recursion
this ._innerUse = true ;
this .href = parts[0] || "" ;
this .protocol = parts[1] || "" ;
//this.host = parts[4] || "";
this .hostname = parts[5] || "" ;
this .port = parts[6] || "" ;
this .pathname = parts[7] || "/" ;
this .search = parts[8] || "" ;
this .hash = parts[10] || "" ;
if (!isIE)
delete this ._innerUse;
else
this ._innerUse = false ;
updateURL.call( this );
}
function updateURL() {
if ( this ._innerUse)
return ;
// Prevent infinite recursion
this ._innerUse = true ;
this .href = this .protocol + '//' + this .host + this .pathname + this .search + this .hash;
if (!isIE)
delete this ._innerUse;
else
this ._innerUse = false ;
}
})()
* This source code was highlighted with Source Code Highlighter .
ゲッター/セッターの作成を検討してください。
- Firefoxの場合:
var keys = [ "href" , "protocol" , "host" , "hostname" , "port" , "pathname" , "search" , "hash" ];
for ( var i = 0, l = keys.length; i < l; i++) {
( function (i) {
var key = keys[i];
var gKey = 'get' + key.substr(0, 1).toUpperCase() + key.substr(1);
var sKey = 'set' + key.substr(0, 1).toUpperCase() + key.substr(1);
URL.prototype.__defineGetter__(key, gs[gKey]);
URL.prototype.__defineSetter__(key, gs[sKey]);
})(i);
}
* This source code was highlighted with Source Code Highlighter .
魔法のURL.prototype.__defineGetter__
およびURL.prototype.__defineSetter__
を使用しURL.prototype.__defineGetter__
。 その結果、疑似属性url.href, url.path
などがあり、ハンドラー関数が実際に呼び出されるものを変更します。
- Internet Explorerの場合:ここでタンバリンと踊り始めます。 IEバージョン<8には、getter / setterメカニズムがまったくありません。 ただし、素晴らしいイベントがあります
onpropertchange
。 使用するものは何もありません。 ただし、複雑な事態が発生します。このイベントはDOM要素にのみ存在し、これらの要素がすでにDOMモデルに含まれている場合にのみ存在します 。 さて、そうします:
var el = document .createElement( 'div' );
el.style.display = 'none' ;
document .body.appendChild(el);
// ...
el.onpropertychange = function (){
var pn = event .propertyName; //
var pv = event .srcElement[ event .propertyName]; //
// ...
}
// ...
return el; // el. .. new URL(...)
// URL, DIV.
// , , ..
// instanceof. , IE, :)
* This source code was highlighted with Source Code Highlighter .
例2
// URL = 'http://my.site.com/somepath/'
var u = new URL( 'relative/path/index.html' )
u.href // my.site.com/somepath/relative/path/index.html
u.href = '/absolute/path.php?a=8#some-hash'
u.href // my.site.com/absolute/path.php?a=8#some-hash
u.hash // #some-hash
u.protocol = 'https:'
u.href // my.site.com/absolute/path.php?a=8#some-hash
u.host = 'another.site.com:8080'
u.href // another.site.com:8080/absolute/path.php?a=8#some-hash
u.port // 8080
// ,
* This source code was highlighted with Source Code Highlighter .
FF3 +、IE6 +で動作します。 Safari / Chrome用にめちゃくちゃにすることができます。 Operaについて-わかりません。 RTFMが必要です。
このように
私は何か有用なことをして、この記事を書いて私の一日を無駄にしないことを望みます:-)
PS:はい、異なるブラウザのゲッターとセッターに関する別の記事を書くと思います。 Firefoxだけが生きているわけではありません(小さなPR:Habrahabrに私の考えの流れを負担させないために-私のブログへようこそ-http://web-by-kott.blogspot.com/。開始)