//最初にセレクターの初期化部分を見てください
//初期化部分は、DOM を操作するために使用するメソッドを決定することであることがわかります。これらのメソッドを見てみましょう
var Selector = Class.create( {
初期化: function(expression) {
this.expression =expression.strip();
if (this.ShouldUseSelectorsAPI()) {
this.mode = 'selectorAPI';
} else if (this. shouldUseXPath()) {
this.mode = 'xpath';
this.compileXPathMatcher(); >this.mode = "通常" ;
this.compileMatcher()
}
//============ ======== ==============================
//XPath、FF をサポートこのメソッド
shouldUseXPath: (function() {
//ブラウザにバグがあるかどうかを確認してみましょう。このバグの具体的な原因はインターネット上で見つかりませんでした。おそらくチェックすることを意味します。特定のノードが正しく見つかるかどうか。 Number
var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
var isBuggy = false;
if (document.evaluate && window.XPathResult) {
var el = document .createElement('div');
el.innerHTML = '
< /div>';
//ここでの local-name() は、名前空間を削除して
var xpath = " を検索することを意味します。//*[local-name()= 'ul' またはローカル名 ()='UL']"
"//*[ローカル名()='li' またはローカル名()='LI']";
// document.evaluate はコア DOM Query メソッドであり、具体的な使用方法はオンラインで検索できます。
var result = document.evaluate(xpath, el, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); (result.snapshotLength != = 2);
el = null>}
return isBuggy>})();
/ /返されたメソッドでこの種の DOM 操作がサポートされているかどうかを判断します。
if (!Prototype.Browser features.XPath) return false;
var e = this.expression;
//ここで、Safari が -of-type 式と空の式をサポートしていないことがわかります。オペレーション
if (Prototype.Browser.WebKit &&
(e.include("-of-type") || e.include(":empty")))
return
if ((/([[w-]*?:|:checked)/).test(e))
return false;
if (IS_DESCENDANT_SELECTOR_BUGGY) return
return true
}
})(),
//===================== ==============================
//Sarafi と opera はこのメソッドをサポートしています
shouldUseSelectorsAPI: function () {
if (!Prototype.BrowserFeature.SelectorsAPI) return false;
//大文字と小文字を区別した検索がサポートされているかどうかを決定します
if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; if (!Selector._div) Selector._div = new Element('div');
//空の div でクエリを実行するときに例外がスローされるかどうかを確認します
try {
Selector._div.querySelector (this.expression);
} catch(e) {
return
}
//================ = ==================================
//Selector.CASE_INSENSITIVE_CLASS_NAMES プロパティ
/ *document.compatMode は、現在のブラウザーで使用されるレンダリング モードを決定するために使用されます。
document.compatMode が BackCompat に等しい場合、ブラウザのクライアント領域の幅は document.body.clientWidth です。
document.compatMode が CSS1Compat に等しい場合、ブラウザのクライアント領域の幅は document.documentElement.clientWidth です。 */
if (Prototype.BrowserFeature.SelectorsAPI &&
document.compatMode === 'BackCompat') {
Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
var div = ドキュメント。 createElement('div'),
span = document.createElement('span');
div.id = "prototype_test_id";
span.className = 'Test'; .appendChild(span);
var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
div = span = null; ();
}
true を返す
}、
//=================== = ==============================
//これら 2 つのどちらでもない場合は、document.getElement( s)By* 一連の処理メソッド。IE8 は SelectorAPI をサポートし始めたようです。IE の他のバージョンでは DOM をクエリするのに通常のメソッドしか使用できません。
//以下は FF でサポートされている shouldUseXPath メソッドになります。 !!!
コードをコピー
コードは次のとおりです:
//XPathをクエリに使用すると判断した場合、compileXPathMatcherメソッドを呼び出す
compileXPathMatcher: function() {
//パターンとxpathは以下に示す
var e = this.expression, ps = Selector.patterns,
x = Selector.xpath, le, m, len = ps.length, name;
//クエリ文字列 e かどうかを判断します。キャッシュされています
if (Selector._cache[e]) {
this.xpath = Selector._cache[e];
}
// './/*' は、現在のノード すべてのノードを表現する方法がわからない場合は、オンラインで XPath 表現メソッドを確認してください。
this.matcher = ['.//*']
//ここでのファイルは、無限ループ検索で、正規表現は 1 つのスペースを除くすべての文字に一致します。
while (e && le != e && (/S/).test(e)) {
le = e; >// パターンを 1 つずつ検索します
for ( var i = 0; i// ここでの名前は、パターン内のオブジェクトの name 属性です
name = ps[i ].name;
//ここで式が一致するかどうかを確認します このパターンの正規表現
if (m = e.match(ps[i].re)) {
/*
注ここで、以下の xpath の一部はメソッドであり、一部は文字列であるため、ここでそれが文字列であるかどうかを判断する必要があり、Template の Evaluate メソッドを呼び出し、内部の #{...} 文字列を置き換える必要があります。がメソッドである場合は、正しいパラメータを渡してメソッド
*/
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
new Template(x[name]).evaluate(m));
//一致する部分を削除し、次の一致する文字列を続けます
e = e.replace(m[0], '');
break;
}
}
}
// 一致するすべての xpath 式を接続して、最終的な xpath クエリ文字列を形成します
this.xpath = this.matcher.join( '');
//キャッシュに入れます
Selector._cache[this.expression] = this.xpath;
},
//======== ================== ===================
//これらのパターンは次の目的で使用されます。対応する式全体に基づいて、クエリ文字列が何を探しているのかを判断します。たとえば、文字列 '#navbar' がパターンに従って一致すると、ID
patterns: [
{ name: 'laterSibling', re : /^s*~s*/ },
{ 名前: '子' , re: /^s*>s*/ },
{ 名前: '隣接', re: /^s* s*/ },
{ name: 'descendant', re: /^s / },
{ name: 'tagName', re: /^s*(*|[w-] )(b| $)?/ },
{ name: 'id', re: /^ #([w-*] )(b|$)/ },
{ name: 'className', re: /^ .([w-*] )(b|$)/ },
{ name: 'pseudo', re:
/^:((first|last|nth|nth-last|only)(- child|-of-type)|empty|checked|(en|d
は )abled|not)(((.*?)))?(b|$|(?=s|[: ~> ]))/ },
{ name: 'attrPresence', re: /^ [((?:[w-] :)?[w-] )]/ },
{ name: 'attr' 、re:
/[((?:[w-]*:)? [w-] )s*(?:([!^$*~|]?=)s*((['"] )([^4]*?)4|([^'"][^
]]*?)))?]/ }
],
//==== ==================== ======================
/*パターンを見つけたら、対応する名前を使用して、対応するクエリ文字列の xpath 表現を見つけます。たとえば、上記の id は id 文字列に対応します。compileXPathMatcher では、xpath が文字列であるかメソッドであるかが判断され、呼び出しには対応するパラメーターが渡されます。 xpath: {
子孫: "/ /*"、
子: "/*"、
隣接: "/following-sibling::*[1]"、
laterSibling: '/following -sibling::*',
tagName: function(m) {
if (m[1] == '*') return '';
return "[local-name()='"; m[1].toLowerCase()
"' または local-name()='" m[1].toUpperCase() "']"
},
className: "[contains(concat) (' ', @class, ' ') , ' #{1} ')]",
id: "[@id='#{1}']",
//…一部省略メソッド
//= ======================================= =====
//以下にセレクターの findElements メソッドを入力してください。 !
コードをコピー
コードは次のとおりです:
findElements: function(root) {
//root が null であるかどうかを判断し、document
root = root document ||
var e = this; .expression, results ;
//DOM の操作に使用されるモードを決定します。FF では xpath
switch (this.mode) {
case 'selectorsAPI':
if (root !== document) {
var oldId = root.id, id = $(root).identify();
id = id.replace(/[.:]/g, "\$0") );
e = "#" id " " e;
}
結果 = $A(root.querySelectorAll(e)).map(Element.extend); id = oldId;
return results;
case 'xpath':
//_getElementsByXPath メソッドを見てみましょう
return document._getElementsByXPath(this.xpath, root); 🎜>デフォルト:
return this.matcher(root)
}
},
//================= ======== ==================
//このメソッドは、見つかったノードを実際に結果に入れて返します。このメソッドの詳細な説明の URL を以下に示します。
if (Prototype.BrowserFeature.XPath) {
document._getElementsByXPath = function(expression,parentElement) {
var results = [] ;
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i )
results.extend(query.snapshotItem(i));
結果を返します。 🎜>以下のURLがdocument.evaluateのメソッドの説明です: https://developer.mozilla.org/cn/DOM/document.evaluate
*/
例を使ってみましょう
まず、$$ で findChildElements メソッドを呼び出し、式を ['#navbar a','#siderbar a'] に設定します。
次の呼び出し: selector = new Selector(expressions[i].strip ()); 新しい Selector オブジェクトを作成し、initialize メソッドを呼び出します。つまり、FF なので、使用する DOM API を決定します。次に、compileXPathMatcher()
を呼び出してから、compileXPathMatcher() var e = this.expression、e を '#navbar a' に設定し、while ループに入り、パターンを走査し、クエリ文字列の一致パターンを確認します。ここではパターンの正規表現に基づいて、 { name: 'id', re : /^#([w-*] )(b|$)/ },、つまり、 m = e.match( の場合、 name は id になります。 ps[i].re) が一致し、m は配列に設定されます。ここで、m[0] は一致した文字列 '#navbar' 全体、m[1] は最初に一致したグループ文字列 'navbar'
次に、Object.isFunction(x[name])を求め、idが文字列に相当するので、new Template(x[name]).evaluate(m))を実行します。 String: id: "[@id='#{1] }']", #{1} は m[1]、つまり 'navbar' に置き換えられ、最後に結果が this.matcher
に入れられます。次に、最初に一致した文字列 e を削除します。は「a」になります、ここはAスペースです!次に、
との照合を続けます。今回の照合は { name: 'descendant', re: /^s/ } で、xpath:descendant: "//*", で対応する子孫項目を見つけます。次に、この文字列を this.matcher に入力し、スペース e を削除すると、文字 'a' だけが残ります。単語
との一致が続きます。一致した単語は次のとおりです。 { name: 'tagName', re: / ^ s*(*|[w-] )(b|$)?/ } 次に、tagName、
tagName: function(m) {
if (m[ 1] = = '*') return '';
return "[local-name()='" m[1].toLowerCase()
"' または local-name()='" m[ 1].toUpperCase() "']";
}
はメソッドであるため、x[name](m) を呼び出し、m[1]='a' を返します。文字列を this.matcher に入力すると、今度は e が空の文字列になり、ループを終了して this.matcher 配列を xpath 文字列に接続します。 [@id='navbar' ]//*[local-name()='a' or local-name()='A']
セレクターを初期化した後、セレクターのインスタンス メソッド findElements を実行します。セレクター: document. _getElementsByXPath(this.xpath, root);
_getElementsByXPath メソッドで実際の DOM クエリ メソッド document.evaluate を実行し、最終的に結果を返します。上記は、FF で DOM をクエリするプロセス全体です。
IE でのプロセスは Opera や Safari でのプロセスと同じですが、具体的な実行方法は少し異なりますので、興味がある方はご自身で学習してください。複雑な DOM 選択操作の例は示しません。ここで構築されたプロセスは、パターン マッチングによる xpath の生成と、そのパターンや xpath の提案など、非常に学ぶ価値があります。
すべてのブラウザと互換性のあるフレームワークを作成するのは簡単ではないことがわかります。学べ、学べ!