>  기사  >  웹 프론트엔드  >  프로토타입 선택기 객체 learning_prototype

프로토타입 선택기 객체 learning_prototype

WBOY
WBOY원래의
2016-05-16 18:49:141087검색
코드 복사 코드는 다음과 같습니다.

function $$() {
return Selector. findChildElements(document , $A(arguments));
}

이 클래스는 세 부분으로 나눌 수 있습니다. 첫 번째 부분은 브라우저에 따라 어떤 DOM 작업 방법을 사용할지 결정하는 것입니다. 그 중 IE를 운영하는 것은 일반적인 getElementBy* 시리즈 메소드를 사용하는 것입니다. FF는 document.evaluate이고 Opera와 Safari는 selectorsAPI입니다. 두 번째 부분은 findElements, match 등과 같이 외부에서 제공되는 기본 함수입니다. Element 개체의 많은 메서드는 이 개체의 메서드를 직접 호출합니다. 세 번째 부분은 XPath와 DOM 쿼리를 위한 기타 일치 기준(예: 첫 번째 자식 검색을 나타내는 문자열, n번째 자식 검색을 나타내는 문자열)입니다.

이 개체에는 메소드가 많기 때문에 소스 코드를 모두 제공하지는 않습니다. 실제로 일부 메소드의 코드만 이해합니다. 여기서는 간단한 예를 사용하여 다양한 브라우저에 따른 DOM 선택 과정을 안내합니다. 이 과정에서 필요한 소스코드를 제시하고 설명합니다.

구체적인 예는 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.


/*먼저 $$를 찾습니다. 이 메소드에서는 위에서 제공한 메소드가 호출되며 Selector의 findChildElements 메소드가 호출되며 첫 번째 매개변수는 document이고 나머지 매개변수는 DOM 쿼리 문자열의 배열입니다*/

findChildElements: function(element , 표현식) {
//여기에서 먼저 분할을 호출하여 문자열 배열을 처리하고, 적합한지 확인하고, 공백을 삭제합니다.
expressions = Selector.split(expressions.join(',')); /handlers에는 concat, Unique 등과 같은 DOM 노드 메소드의 일부 처리가 포함되어 있습니다.
var results = [], h = Selector.handlers
//쿼리 표현식을 하나씩 처리합니다.
for (var i = 0, l = 표현식.길이, 선택기; i < l; i ) {
//새 선택기
선택기(표현식[i].strip())//연결 쿼리된 노드를 결과로
h.concat(results, selector.findElements(element));
}
//발견된 노드 수가 1보다 크면 중복 노드를 필터링합니다.
return (l > 1) ? h.unique (결과) : 결과
}

//=================== ========== ===================

//Selector.split 메서드:
split: function(expression ) {
var 표현식 = [] ;
expression.scan(/(([w#:.~> ()s-] |*|[.*?]) )s*(,|$ )/, function(m) {
           //alert(m[1])
expressions.push(m[1].strip())
return 표현식;
}

//===================================== ========== ===

//Selector.handlers 객체
handlers: {
concat: function(a, b) {
for (var i = 0, node = b[i ]; i )
a.push(node);
return a;
//...일부 메소드 생략
: function(nodes) {
if (nodes.length == 0) return node;
var results = [], n
for (var i = 0, l = node.length; i < ; l; i )
if ( typeof (n = 노드[i])._countedByPrototype == '정의되지 않음') {
n._countedByPrototype = Prototype.emptyFunction
results.push(Element.extend( n));
return Selector.handlers.unmark(results);
},

//이제 Selector 객체를 생성하는 과정으로 넘어갑니다. !






코드 복사

코드는 다음과 같습니다.

//Selector의 초기화 부분을 먼저 살펴보세요
//DOM을 어떤 메소드로 동작시킬 것인지 결정하는 부분이 초기화 부분임을 알 수 있습니다
var Selector = Class.create( {
initialize: function(expression) {
this.expression = 표현식.strip();

if (this.shouldUseSelectorsAPI()) {
this.mode = 'selectorsAPI';
} else if (this.shouldUseXPath()) {
this.mode = 'xpath';
this.compileXPathMatcher(); >this.mode = "normal" ;
this.compileMatcher()
}

}

//============ ======== ===============================

//XPath, FF 지원 이 메소드는
shouldUseXPath: (function() {

//브라우저에 BUG가 있는지 확인해 보겠습니다. 인터넷에서 이 BUG의 구체적인 원인을 찾지 못했습니다. 아마도 확인한다는 의미일 것입니다. 특정 노드를 올바르게 찾을 수 있는지 여부 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' 또는 local-name ()='UL']"
    "//*[local-name()='li' 또는 local-name()='LI']";
    // document.evaluate는 핵심 DOM 쿼리 방법이며, 구체적인 사용법은 온라인으로 검색할 수 있습니다.
    var result = document.evaluate(xpath, el, null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)

    isBuggy = (result.snapshotLength != = 2);
    el = null;
    }
    return isBuggy;
    })(); /Judge 반환된 메서드에서 이러한 종류의 DOM 작업이 지원되는지 여부입니다.
    if (!Prototype.BrowserFeatures.XPath) return false;

    var e = this.expression
    //Safari는 -of-type 표현식과 빈 표현식을 지원하지 않는다는 것을 알 수 있습니다. 작업
    if (Prototype.Browser.WebKit &&
    (e.include("-of-type") || e.include(":empty")))
    return false; 🎜>if ((/([[w-]*?:|:checked)/).test(e))
    false 반환

    if (IS_DESCENDANT_SELECTOR_BUGGY) 반환 false

    true 반환
    }

    })(),

    //==================== ==============================

    //Sarafi와 Opera는 이 방법을 지원합니다
    shouldUseSelectorsAPI: 함수 () {
    if (!Prototype.BrowserFeatures.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 false
    }

    //=============== = ==================================

    //Selector.CASE_INSENSITIVE_CLASS_NAMES 속성
    / *document.compatMode는 현재 브라우저에서 사용되는 렌더링 모드를 결정하는 데 사용됩니다.
    document.compatMode가 BackCompat과 같으면 브라우저 클라이언트 영역 너비는 document.body.clientWidth입니다.
    document.compatMode가 CSS1Compat과 같으면 브라우저 클라이언트 영역 너비는 document.documentElement.clientWidth입니다. */

    if (Prototype.BrowserFeatures.SelectorsAPI &&
    document.compatMode === 'BackCompat') {
    Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
    var div = document. 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;
    return isIgnored; ();
    }

    true를 반환합니다.
    },

    //================== = ==============================

    //둘 중 어느 것도 아닌 경우 document.getElement( s)* 일련의 처리 방법에 따라 IE8에서는 SelectorAPI를 지원하기 시작한 것으로 보입니다. 다른 버전의 IE에서는 DOM

    을 쿼리하는 데 일반 방법만 사용할 수 있습니다.//다음은 FF에서 지원하는 shouldUseXPath 방법입니다. !!!






    코드 복사


    코드는 다음과 같습니다
    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]; return
    }
    // './/*'는 현재 노드 모든 노드를 표현하는 방법을 모른다면 온라인에 가서 XPath 표현 방법을 살펴보세요.
    this.matcher = ['.//*']//여기에 있는 파일은 방지합니다. 무한 루프 검색 및 정규식은 공백 하나만 제외하고 일치합니다.
    while (e && le != e && (/S/).test(e)) {
    le = e; >//하나씩 패턴 찾기
    for ( var i = 0; i//여기서 이름은 패턴
    name = ps[i에 있는 객체의 이름 속성입니다. ].name;//여기서 표현식이 일치하는지 확인합니다. 이 패턴의 정규 표현식      
    if (m = e.match(ps[i].re)) {
    /*
    참고 여기에서 아래 xpath 중 일부는 메소드이고 일부는 문자열이므로 여기서 판단해야 합니다. 문자열인 경우 Template의 평가 메소드를 호출하고 내부에 있는 #{...} 문자열을 교체해야 합니다. 메소드인 경우 올바른 매개변수를 전달하여 메소드
    */
    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*/ },
    { 이름: 'child' , re: /^s*>s*/ },
    { 이름: '인접', re: /^s* s*/ },
    { 이름: 'descendant', re: /^s / },
    { 이름: 'tagName', re: /^s*(*|[w-] )(b| $)?/ },
    { 이름: 'id', re: /^ #([w-*] )(b|$)/ },
    { 이름: 'className', re: /^ .([w-*] )(b|$)/ },
    { 이름: '의사', re:
    /^:((first|last|nth|nth-last|only)(- child|-of-type)|비어 있음|선택됨|(en|d
    가능하지 않음|not)(((.*?)))?(b|$|(?=s|[: ~> ]))/ },
    { 이름: 'attrPresence', re: /^ [((?:[w-] :)?[w-] )]/ },
    { 이름: 'attr' , 다시:
    /[((?:[w-]*:)? [w-] )s*(?:([!^$*~|]?=)s*((['"] )([^4]*?)4|([^'"][^
    ]]*?)))?]/ }
    ],

    //==== ===========================================

    /* 패턴을 찾은 후 해당 이름을 사용하여 해당 쿼리 문자열의 xpath 표현을 찾습니다. 예를 들어 위의 id는 id 문자열에 해당합니다. compileXPathMatcher에서는 xpath가 문자열인지 메소드인지 판단하여 해당 매개변수를 호출에 전달합니다.*/
    xpath: {
    descendant: "/ /*",
    child: "/*",
    adjacent: "/following-sibling::*[1]",
    laterSibling: '/following -sibling::*',
    태그 이름: 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}']",
    //...일부 생략 방법

    //= ======================================= =====

    //아래 Selector의 findElements 메소드를 입력하세요! !






    코드 복사


    코드는 다음과 같습니다.

    findElements: function(root) {
    //root가 null인지 확인합니다. null인 경우 document
    root = root || 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;

    }
    results = $A(root.querySelectorAll(e)).map(Element.extend); id = oldId;

    return results;
    case 'xpath':
    //_getElementsByXPath 메소드를 살펴보겠습니다.
    return document._getElementsByXPath(this.xpath, root); 🎜>기본값:
    return this.matcher(root)
    }
    },

    //================ ======== =================

    //이 메소드는 실제로 발견된 노드를 결과에 넣고 Document.evaluate를 반환합니다. 여기서는 이 메서드에 대한 자세한 설명을 위한 URL이 아래에 나와 있습니다.
    if (Prototype.BrowserFeatures.XPath) {
    document._getElementsByXPath = function(expression, parentElement) {
    var results = [] ;
    var query = document.evaluate(expression, $(parentElement) || document,
    null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    for (var i = 0, length = query.snapshotLength; i < 길이; i )
    results.push(Element.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 객체를 생성하고, 초기화 메소드를 호출합니다. 즉, 어떤 DOM API를 사용할 것인지 결정합니다. 그런 다음 compileXPathMatcher()

    를 호출하고 compileXPathMatcher() var e = this.expression을 호출하고 e를 '#navbar a'로 설정한 다음 while 루프를 입력하고 패턴을 탐색하고 쿼리 문자열의 일치 패턴을 확인합니다. 여기서는 패턴의 정규식을 기반으로 { name: 'id', re : /^#([w-*] )(b|$)/ }를 찾으므로 m = e.match(인 경우 이름은 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} in은 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에 해당하는 xpath 항목을 찾습니다.

    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가 빈 문자열입니다. while의 첫 번째 조건은 충족되지 않습니다. 루프를 종료하고 this.matcher 배열을 xpath 문자열에 연결합니다. [@id='navbar' ]//*[local-name()='a' or local-name()='A']

    Selector 초기화 후 인스턴스 메소드 findElements를 실행합니다. 여기에서 직접 호출하세요: document._getElementsByXPath(this.xpath, root);

    _getElementsByXPath 메서드에서 실제 DOM 쿼리 메서드 document.evaluate를 실행하고 마지막으로 결과를 반환합니다.

    위는 FF에서 DOM을 쿼리하는 전체 과정입니다!

    IE에서의 프로세스는 Opera 및 Safari와 동일하지만 구체적인 실행 방법이 약간 다릅니다. 관심이 있는 경우 직접 학습해도 됩니다. 복잡한 DOM 선택 작업의 예는 제공하지 않습니다. 여기에 구축된 프로세스는 패턴 매칭을 통해 xpath를 생성하고 해당 패턴을 제안하는 xpath 등을 포함하여 매우 배울 가치가 있습니다.

    모든 브라우저에서 호환되는 프레임워크를 작성하는 것이 쉽지 않다는 것을 알 수 있습니다! 배워라, 배워라!
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.