使用 Greasemonkey 時會遇到的功能最為強大的一個工具就是 evaluate 函數。通過使用XPath這種查詢語言,它可以用來尋找頁面中的元素,屬性和文本。
舉個例子來說,如果您想獲得某個頁面上的全部鏈接。您也許會想到使用document.getElementsByTagName('a');但是如果您還要繼續檢查是否每個鏈接都具有href屬性,因為<a>還可以用來作為錨名稱使用,這時,您需要使用Firefox內建的XPath 支持去獲取全部具有href屬性的<a>元素。
例子: 獲取頁面上的全部鏈接
var allLinks, thisLink;
allLinks = document.evaluate(
'//a[@href]',
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);
for (var i = 0; i < allLinks.snapshotLength; i++) {
thisLink = allLinks.snapshotItem(i);
// do something with thisLink
}
這里,document.evaluate 是關鍵的部分。 它把 XPath 查詢語句作為一個字符串,其它的參數稍后再做解釋。 這條 XPath 查詢語句可以找到全部具有href屬性的<a>元素,并將它們按照隨機的順序依次返回。(這就是說,第一個被返回的元素并一定也是頁面上的第一個這樣的元素。) 隨后,您就可以用 allLinks.snapshotItem(i) 函數訪問每一個元素。
XPath表達式所能做到的甚至會使您驚訝。請看下面這個例子,它獲取了全部具有title屬性的元素。
例子: 獲取全部具有title屬性的元素
var allElements, thisElement;
allElements = document.evaluate(
'//*[@title]',
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);
for (var i = 0; i < allElements.snapshotLength; i++) {
thisElement = allElements.snapshotItem(i);
switch (thisElement.nodeName.toUpperCase()) {
case 'A':
// this is a link, do something
break;
case 'IMG':
// this is an image, do something else
break;
default:
// do something with other kinds of HTML elements
}
}
如果您已經引用了某個元素(例如上面的 thisElement),您就可以用 thisElement.nodeName 來替代它所對應的在 HTML 頁面中的標簽名稱。如果被訪問的這個頁面是以 text/html 的方式被服務器執行, 那么標簽名稱總是用大寫子母返回,不論它在原始頁面是如何定義的。 如果頁面是 application/xhtml+xml 方式的, 那么標簽名稱就會以小寫子母返回。 不論哪種情況,我總是用 thisElement.nodeName.toUpperCase() 得到大寫的標簽名稱。
這是另外一個 XPath 查詢,它返回了 div 中的一個特殊的類。
例子: 獲取 div 中的 sponsoredlink 類
var allDivs, thisDiv;
allDivs = document.evaluate(
"http://div[@class='sponsoredlink']",
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);
for (var i = 0; i < allDivs.snapshotLength; i++) {
thisDiv = allDivs.snapshotItem(i);
// do something with thisDiv
}
注意我在 XPath 查詢語句外使用了雙引號,這樣在語句內部就可以使用單引號。
在 document.evaluate 函數中有很多參數。第二個參數 (在前兩個例子中都是docoment) 可以是一個元素, XPath 查詢只返回包含在這個元素內的元素。所以,如果您已經引用了一個元素(比如, 通過 document.getElementById 或者 通過 document.getElementsByTagName 得到的數組中的一個元素), 那么您就可以限制查詢只返回這個元素的子元素。
第三個參數是對一個叫做 namespace resolver 函數的引用, 它只有在工作在 application/xhtml+xml 類型的頁面上的用戶腳本中是有效的。即使您對它不是很了解也沒有關系,因為那種類型的頁面不是很多,您可能一次也遇不到。如果您很想知道它是如何使用的,請參考 Mozilla XPath documentation (http://www-jcsu.jesus.cam.ac.uk/~jg307/mozilla/xpath-tutorial.html),那里解釋了它的用法。
第四個參數是結果的返回方式。在前面的兩個例子中都使用了 XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, 它將結果以隨機的方式返回。我使用的幾乎全部都是這種方式,但是,出于某種原因,您想讓結果以它們在頁面上出現的順序返回,您可以使用 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE 這種方式。 Mozilla XPath documentation (http://www-jcsu.jesus.cam.ac.uk/~jg307/mozilla/xpath-tutorial.html)還給出了另外的一些用例。
第五個參數用來合并兩次 XPath 查詢的結果。 在獲得第一次調用 document.evaluate 得到的結果之后,它將兩次查詢的結果一起返回。在前面的兩個例子中,這個參數都用了null,這意味著我們只想獲得本次查詢的結果。
現在您明白了嗎?XPath 既可以很簡單,也可以很難,這取決于您要如何使用它。在此我強烈推薦您盡快去閱讀 this excellent XPath tutorial (http://www.zvon.org/xxl/XPathTutorial/General/examples.html),從而了解更多的 XPath 語法。至于 document.evaluate 函數的其它參數, 我幾乎從來不使用它們。事實上,您可以自己定義一個函數來封裝它們。
例子: 自定義的 xpath 函數
function xpath(query) {
return document.evaluate(query, document, null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}
在定義了這個函數之后,您就可以調用 xpath('//a[@href]') 來獲得某個頁面上的全部鏈接, 或者調用 xpath('//* [@title]') 來獲得具有 title 屬性的元素。您仍然需要通過 snapshotItem 函數來訪問結果中的每一項,這個函數的類型并不是一個規則的Javascript數組。