今回は、
jQuery.expr
1360行目からは、
具体的な説明に入る前に、
1364行目からは、
1360: jQuery.extend({
1361: expr: {
1362: "": "m[2]=='*'||jQuery.nodeName(a,m[2])",
1363: "#": "a.getAttribute('id')==m[2]",
1364: ":": {
1365: // Position Checks
1366: lt: "i<m[3]-0",
1367: gt: "i>m[3]-0",
1368: nth: "m[3]-0==i",
1369: eq: "m[3]-0==i",
1370: first: "i==0",
1371: last: "i==r.length-1",
1372: even: "i%2==0",
1373: odd: "i%2",
1374:
1375: // Child Checks
1376: "first-child": "a.parentNode.getElementsByTagName('*')[0]==a",
1377: "last-child": "jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a",
1378: "only-child": "!jQuery.nth(a.parentNode.lastChild,2,'previousSibling')",
1379:
1380: // Parent Checks
1381: parent: "a.firstChild",
1382: empty: "!a.firstChild",
1383:
1384: // Text Check
1385: contains: "(a.textContent||a.innerText||jQuery(a).text()||'').indexOf(m[3])>=0",
1386:
1387: // Visibility
1388: visible: '"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',
1389: hidden: '"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',
1390:
1391: // Form attributes
1392: enabled: "!a.disabled",
1393: disabled: "a.disabled",
1394: checked: "a.checked",
1395: selected: "a.selected||jQuery.attr(a,'selected')",
1396:
1397: // Form elements
1398: text: "'text'==a.type",
1399: radio: "'radio'==a.type",
1400: checkbox: "'checkbox'==a.type",
1401: file: "'file'==a.type",
1402: password: "'password'==a.type",
1403: submit: "'submit'==a.type",
1404: image: "'image'==a.type",
1405: reset: "'reset'==a.type",
1406: button: '"button"==a.type||jQuery.nodeName(a,"button")',
1407: input: "/input|select|textarea|button/i.test(a.nodeName)",
1408:
1409: // :has()
1410: has: "jQuery.find(m[3],a).length",
1411:
1412: // :header
1413: header: "/h\\d/i.test(a.nodeName)",
1414:
1415: // :animated
1416: animated: "jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length"
1417: }
1418: },
1419:
1365行目に頻繁に現われるiはマッチした要素の中で何番目かを表すものです。例えば、
1375行目からは親要素/子要素があるかどうか、
1397行目からは、
jQuery.parse(セレクタ式評価用の正規表現)
1420: // The regular expressions that power the parsing engine
1421: parse: [
1422: // Match: [@value='test'], [@foo]
1423: /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
1424:
1425: // Match: :contains('foo')
1426: /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
1427:
1428: // Match: :even, :last-chlid, #id, .class
1429: new RegExp("^([:.#]*)(" + chars + "+)")
1430: ],
1431:
1420行目からは、
ここでは3つの正規表現が定義されていて、
jQuery.multiFilter()
1432: multiFilter: function( expr, elems, not ) {
1433: var old, cur = [];
1434:
1435: while ( expr && expr != old ) {
1436: old = expr;
1437: var f = jQuery.filter( expr, elems, not );
1438: expr = f.t.replace(/^\s*,\s*/, "" );
1439: cur = not ? elems = f.r : jQuery.merge( cur, f.r );
1440: }
1441:
1442: return cur;
1443: },
1444:
1432行目からのjQuery.
jQuery.find()
1445行目からのjQuery.
1445: find: function( t, context ) {
1446: // Quickly handle non-string expressions
1447: if ( typeof t != "string" )
1448: return [ t ];
1449:
1450: // check to make sure context is a DOM element or a document
1451: if ( context && context.nodeType != 1 && context.nodeType != 9)
1452: return [ ];
1453:
1454: // Set the correct context (if none is provided)
1455: context = context || document;
1456:
1457: // Initialize the search
1458: var ret = [context], done = [], last, nodeName;
1459:
1447行目ですが、
1460: // Continue while a selector expression exists, and while
1461: // we're no longer looping upon ourselves
1462: while ( t && last != t ) {
1463: var r = [];
1464: last = t;
1465:
1466: t = jQuery.trim(t);
1467:
1468: var foundToken = false;
1469:
1464行目でlastの値として、
1470: // An attempt at speeding up child selectors that
1471: // point to a specific element tag
1472: var re = quickChild;
1473: var m = re.exec(t);
1474:
1475: if ( m ) {
1476: nodeName = m[1].toUpperCase();
1477:
1478: // Perform our own iteration and filter
1479: for ( var i = 0; ret[i]; i++ )
1480: for ( var c = ret[i].firstChild; c; c = c.nextSibling )
1481: if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
1482: r.push( c );
1483:
1484: ret = r;
1485: t = t.replace( re, "" );
1486: if ( t.indexOf(" ") == 0 ) continue;
1487: foundToken = true;
1470行目からは、
まず、
最後に、
1488: } else {
1489: re = /^([>+)\s*(\w*)/i;
1490:
1491: if ( (m = re.exec(t)) != null ) {
1492: r = [];
1493:
1494: var merge = {};
1495: nodeName = m[2].toUpperCase();
1496: m = m[1];
1497:
1498: for ( var j = 0, rl = ret.length; j < rl; j++ ) {
1499: var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
1500: for ( ; n; n = n.nextSibling )
1501: if ( n.nodeType == 1 ) {
1502: var id = jQuery.data(n);
1503:
1504: if ( m == "~" && merge[id] ) break;
1505:
1506: if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
1507: if ( m == "~" ) merge[id] = true;
1508: r.push( n );
1509: }
1510:
1511: if ( m == "+" ) break;
1512: }
1513: }
1514:
1515: ret = r;
1516:
1517: // And remove the token
1518: t = jQuery.trim( t.replace( re, "" ) );
1519: foundToken = true;
1520: }
1521: }
1522:
1488行目からは、
- '>'は、
「A > B」 とあった場合にAの子要素を選択するセレクタです。 - '~'は、
「A ~ B」 とあった場合にAの後に続く兄弟要素を選択するセレクタです。 - '+'は、
「A + B」 とあった場合にAの後に続くBを選択するセレクタです。
以上を踏まえた上で、
1489行目の正規表現で'> foo','~ foo','+ foo'のような文字列にマッチするかどうか判定し、
1504行目では、
1523: // See if there's still an expression, and that we haven't already
1524: // matched a token
1525: if ( t && !foundToken ) {
1526: // Handle multiple expressions
1527: if ( !t.indexOf(",") ) {
1528: // Clean the result set
1529: if ( context == ret[0] ) ret.shift();
1530:
1531: // Merge the result sets
1532: done = jQuery.merge( done, ret );
1533:
1534: // Reset the context
1535: r = ret = [context];
1536:
1537: // Touch up the selector string
1538: t = " " + t.substr(1,t.length);
1539:
1523行目からは、
1540: } else {
1541: // Optimize for the case nodeName#idName
1542: var re2 = quickID;
1543: var m = re2.exec(t);
1544:
1545: // Re-organize the results, so that they're consistent
1546: if ( m ) {
1547: m = [ 0, m[2], m[3], m[1] ];
1548:
1549: } else {
1550: // Otherwise, do a traditional filter check for
1551: // ID, class, and element selectors
1552: re2 = quickClass;
1553: m = re2.exec(t);
1554: }
1555:
1556: m[2] = m[2].replace(/\\/g, "");
1557:
1558: var elem = ret[ret.length-1];
1559:
1545行目からは、
1560: // Try to do a global search by ID, where we can
1561: if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
1562: // Optimization for HTML document case
1563: var oid = elem.getElementById(m[2]);
1564:
1565: // Do a quick check for the existence of the actual ID attribute
1566: // to avoid selecting by the name attribute in IE
1567: // also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
1568: if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
1569: oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
1570:
1571: // Do a quick check for node name (where applicable) so
1572: // that div#foo searches will be really fast
1573: ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
1561行目からは、
1574: } else {
1575: // We need to find all descendant elements
1576: for ( var i = 0; ret[i]; i++ ) {
1577: // Grab the tag name being searched for
1578: var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
1579:
1580: // Handle IE7 being really dumb about
1581: if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
1582: tag = "param";
1583:
1584: r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
1585: }
1586:
1574行目からは、
1587: // It's faster to filter by class and be done with it
1588: if ( m[1] == "." )
1589: r = jQuery.classFilter( r, m[2] );
1590:
1591: // Same with ID filtering
1592: if ( m[1] == "#" ) {
1593: var tmp = [];
1594:
1595: // Try to find the element with the ID
1596: for ( var i = 0; r[i]; i++ )
1597: if ( r[i].getAttribute("id") == m[2] ) {
1598: tmp = [ r[i] ];
1599: break;
1600: }
1601:
1602: r = tmp;
1603: }
1604:
1605: ret = r;
1606: }
1607:
1608: t = t.replace( re2, "" );
1609: }
1610:
1611: }
1612:
1587行目は、
最後に1608行目で検索対象の文字列を削除して終了です。
1613: // If a selector string still exists
1614: if ( t ) {
1615: // Attempt to filter it
1616: var val = jQuery.filter(t,r);
1617: ret = r = val.r;
1618: t = jQuery.trim(val.t);
1619: }
1620: }
1621:
ここまで処理を行って、
1622: // An error occurred with the selector;
1623: // just return an empty set instead
1624: if ( t )
1625: ret = [];
1626:
1627: // Remove the root context
1628: if ( ret && context == ret[0] )
1629: ret.shift();
1630:
1631: // And combine the results
1632: done = jQuery.merge( done, ret );
1633:
1634: return done;
1635: },
1636:
エラーが起こった場合は、