Method XbpHTMLViewer2():queryByXPath() Foundation

Gets information about HTML elements.

Syntax
:queryByXPath( <cQuery> ) --> oAsyncResult
Parameters
<cQuery>
A query string specifying the desired elements in XPath notation. If no string is passed, the complete document is returned.
Return

A AsyncResult() object which can be used to wait for the query operation to complete, and for retrieving the operation result. The operation result is an array of DataObjects, one for each matching node in the DOM tree. When querying the complete document, the result is an array with a data object representing the document's root node. If the method fails, the returned AsyncResult object will be in error state. Use :error() to determine more information about the error.

The data objects returned at least have the members :nodeName, :nodeValue, :attributes, :childNodeCount and :children. Attributes are returned as members of a data object in :attributes, with the attribute values stored in a member with the same name as the attribute.

Description

Using the method :queryByXPath(), a query can be performed against the DOM (document object model) tree of the current document for getting information about specific HTML elements. To do this, an XPath query string can be specified for selecting the desired elements. XPath is an expression language allowing the hierarchic addressing of nodes in a tree, see https://www.w3.org/TR/1999/REC-xpath-19991116.

The operation result of a query executed via :queryByXPath() is a read-only representation of the HTML elements matching the query expression. This can be used to check for the existence of certain HTML elements as well as for examining attribute values. Changing page elements via the operation result is not possible. Use :executeScript() or some other means to achieve this.

:queryByXPath() executes asynchronously in the context of the current page. The page must be fully loaded and the DOM tree must be stable for the method to function properly. If the HTML viewer is currently in the process of navigating to another page, for example, querying the DOM tree may fail or produce unexpected results. In this case, use the result object returned by :navigate() or the :navigateComplete() event to wait for navigation to complete before executing your query.

Example for using :queryByXPath()

// This example issues a query for a book in Amazon.com and returns 
// information on the first item found. This is an examplary use-case 
// for using XPath queries for locating known items in a known 
// webpage loaded into a XbpHTMLViewer2 instance. 
// 
// Note that the queries in the example rely on the structure of the 
// Amazon webpage, which may change over time! 
#include "xbp.ch" 

#define BOOK_ISBN       "978-0618640157" 
#define ISBN_SEARCH_URL "https://www.amazon.com/s?k=" 
#define CRLF            Chr(13) + Chr(10) 

PROCEDURE Main() 
LOCAL oDlg 
LOCAL oViewer2 
LOCAL oSettings 
LOCAL oData 
LOCAL cUrl 

  SET CHARSET TO ANSI 

  // Create a form for hosting the HTML viewer object 
  oDlg := XbpDialog():new( AppDesktop() ) 
  oDlg:taskList := .T. 
  oDlg:title    := "XbpHTMLViewer2:queryByXPath() example" 
  oDlg:create( ,,, {640,480} ) 
  CenterControl( oDlg ) 

  // Create the HTML viewer object in the form's content area 
  oViewer2 := XbpHTMLViewer2():new( oDlg:drawingArea ) 
  oViewer2:layoutAlign := XBPLAYOUT_LEFT + XBPLAYOUT_TOP + XBPLAYOUT_RIGHT + XBPLAYOUT_BOTTOM 
  oViewer2:create( ,, {10,10}, {600,420} ) 

  // Change the user agent to a standard string. Amazon.com 
  // may not allow the search otherwise 
  oSettings := CoreWebView2Settings():new() 
  oSettings:UserAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" 
  oViewer2:setSettings( oSettings ) 

  // Navigate to the results page on Amazon.com and extract 
  // information for the first item found 
  cUrl := ISBN_SEARCH_URL + BOOK_ISBN 
  oViewer2:navigate( cUrl ):wait() 
  oData := GetItemData( oViewer2 ) 

  // Display the information in a message box 
  DisplayResults( oData ) 

  oDlg:showModal() 
  oDlg:destroy() 
RETURN 


// Use XPath queries to extract ASIN, title and price information 
// of the first book found. Return is a data object with the 
// members :asin, :title and :price. 
FUNCTION GetItemData( oViewer2 ) 
LOCAL oRet := DataObject():new() 
LOCAL aRet 

  oRet:asin  := "" 
  oRet:title := "" 
  oRet:price := "" 

  // 
  // This is the XPath query for the first search result: 
  //   "//div[@data-component-type='s-search-result'][1]" 
  // 
  // The actual information like the book title is located in 
  // childs of this HTML element. Note that the used XPath 
  // queries heavily depend on the structure of the webpage 
  // that is examined! 
  // 

  // Get the ASIN which is specified as an attribute value 
  // and isn't visible to the user 
  aRet := oViewer2:queryByXPath( "//div[@data-component-type='s-search-result'][1][@data-asin and @role='listitem']" ):get() 
  IF .NOT. Empty(aRet) 
     oRet:asin := aRet[1]:attributes:getNoIvar("data-asin") 
  ENDIF 

  // Get the book title which is simple text in a child element 
  aRet := oViewer2:queryByXPath( "//div[@data-component-type='s-search-result'][1]//h2//span/text()" ):get() 
  IF .NOT. Empty(aRet) 
     oRet:title := aRet[1]:nodeValue 
  ENDIF 

  // Get the price which is simple text in a child element 
  aRet := oViewer2:queryByXPath( "//div[@data-component-type='s-search-result'][1]//span[@class='a-price']//span[@class='a-offscreen']/text()" ):get() 
  IF .NOT. Empty(aRet) 
     oRet:price := aRet[1]:nodeValue 
  ENDIF 
RETURN oRet 


PROCEDURE DisplayResults( oData ) 
LOCAL cMsg 

  IF Empty(oData:asin) 
     cMsg := "No book with ISBN " + BOOK_ISBN + " was found." 
  ELSE 
     cMsg := "Amazon Id (ASIN): " + oData:asin + CRLF +; 
             "Book Title: " + oData:title + CRLF +; 
             "Price: " + oData:price 
  ENDIF 

  MsgBox( cMsg, "First search result:" ) 
RETURN 


PROCEDURE AppSys() 
  // Prevent creation of the default console window 
RETURN 

Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.