Method XbpHTMLViewer2():queryByClass() Foundation

Gets the HTML element with a certain CSS class.

Syntax
:queryByClass( <cClass> ) --> oAsyncResult
Parameters
<cClass>
The CSS class name or names that must be set for the HTML element. If more than one class name is to be included in the query, the names must be separated with a whitespace.
Return

A AsyncResult() object which can be used to wait for the query operation to complete, and for retrieving the operation result. This is either a representation of a HTML element matched in the quest or the value NIL. If the method fails, the returned AsyncResult object will be in error state. Use :error() to determine more information about the error.

The returned data object at least has 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

The method :queryByClass() returns information about the HTML element that has the specified CSS class(es) set in its "class" attribute.

If a matching HTML element is found, :queryByClass() returns a read-only representation of the HTML element as the operation result. If more than one element matches the criteria, only a representation of the first element is returned. 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.

:queryByClass() executes asynchronously in the context of the current page. The page must be fully loaded and the DOM (document object model) 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 performing the query. To immediately retrieve the operation result, use the :get() method of the AsyncResult() object returned by :queryByClass().

Example for using :queryByClass() and :queryAllByClass()

// This example demonstrates extracting data from a HTML page 
// containing an invoice. The example uses :queryByClass() and 
// :queryAllByClass() to locate the corresponding HTML elements 
// via its associated CSS class. 
#include "xbp.ch" 

#define CRLF   Chr(13)+Chr(10) 

PROCEDURE Main() 
LOCAL oDlg 
LOCAL oViewer2 := NIL 
LOCAL oXbp 

  SET CHARSET TO ANSI 

  // Create a form for hosting the HTML viewer object and buttons 
  // for displaying the query results 
  oDlg := XbpDialog():new( AppDesktop() ) 
  oDlg:taskList := .T. 
  oDlg:title    := "XbpHTMLViewer2:query(All)ByClass() example" 
  oDlg:create( ,,, {750,540} ) 
  CenterControl( oDlg ) 

  oXbp := XbpPushButton():new( oDlg:drawingArea ) 
  oXbp:caption  := "Invoice Totals" 
  oXbp:activate := {|| DisplayTotals(oViewer2)} 
  oXbp:layoutAlign := XBPLAYOUT_RIGHT + XBPLAYOUT_BOTTOM 
  oXbp:create( ,, {620,60}, {100,40} ) 

  oXbp := XbpPushButton():new( oDlg:drawingArea ) 
  oXbp:caption  := "Bill to Address" 
  oXbp:activate := {|| DisplayBillToAddress(oViewer2)} 
  oXbp:layoutAlign := XBPLAYOUT_RIGHT + XBPLAYOUT_BOTTOM 
  oXbp:create( ,, {620,10}, {100,40} ) 

  // 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,480} ) 

  // Load the invoice into the HTML viewer 
  DisplayInvoice( oViewer2, "INV-2024-001" ) 

  oDlg:showModal() 
  oDlg:destroy() 
RETURN 

// Load an invoice into the HTML viewer 
PROCEDURE DisplayInvoice( oViewer2, cId ) 
LOCAL cHTML 

  cHTML := LoadInvoice( cId ) 
  oViewer2:setHTML( cHTML ) 
RETURN 

// Display bill to address in a message box 
PROCEDURE DisplayBillToAddress( oViewer ) 
LOCAL cAddress 

  cAddress := GetBillToAddress( oViewer ) 

  IF .NOT. Empty(cAddress) 
     MsgBox( "The address is: " + CRLF + CRLF + cAddress, ":query(All)ByClass() example" ) 
  ENDIF 
RETURN 

// Extract bill to address data from the HTML page 
FUNCTION GetBillToAddress( oViewer2 ) 
LOCAL oNode 
LOCAL oP 

  // Use the CSS class for formatting the addresses to 
  // locate the desired HTML elements. The CSS class must 
  // be known in advance. The HTML page has the following 
  // structure: 
  //  <div class="addresses"> 
  //   <div class="address-block"> 
  //     .. bill to address .. 
  //   </div> 
  //   <div class="address-block"> 
  //     .. ship to address .. 
  //   </div> 
  // 
  // We'll use :queryByClass() to get the first element 
  // with class "address-block". This is equivalent to 
  // :queryAllByClass("address-block")[1]. 
  oNode := oViewer2:queryByClass( "address-block" ):get() 
  IF Len(oNode:children) < 2 
     RETURN "" 
  ENDIF 

  oP := oNode:children[2] 
RETURN ExtractAddressData(oP) 

// Extract the address data from the specified <p> HTML 
// element. The data is structured as follows: 
//   <strong>.. addressee ..</strong><br> 
//   .. line 1 ..<br> 
//   .. line 2 ..<br> 
FUNCTION ExtractAddressData( oP ) 
LOCAL oText 
LOCAL cAddress := "" 

  // (<strong>) 
  oText := oP:children[1]:children[1] 
  cAddress += LTrim( oText:nodeValue ) + CRLF 

  // (line #1) 
  oText := oP:children[3] 
  cAddress += LTrim( StrTran(oText:nodeValue, Chr(10), "") ) + CRLF 

  // (line #2) 
  oText := oP:children[5] 
  cAddress += LTrim( StrTran(oText:nodeValue, Chr(10), "") ) + CRLF 

  // (line #3) 
  oText := oP:children[7] 
  cAddress += LTrim( StrTran(oText:nodeValue, Chr(10), "") ) + CRLF 

  // (line #4) 
  oText := oP:children[9] 
  cAddress += LTrim( StrTran(oText:nodeValue, Chr(10), "") ) 
RETURN cAddress 

// Display invoice sums in a message box 
PROCEDURE DisplayTotals( oViewer ) 
LOCAL cTotals 

  cTotals := GetTotals( oViewer ) 

  IF .NOT. Empty(cTotals) 
     MsgBox( "The invoice totals are: " + CRLF + CRLF + cTotals, ":query(All)ByClass() example" ) 
  ENDIF 
RETURN 

// Extract invoice totals from the HTML page 
FUNCTION GetTotals( oViewer2 ) 
LOCAL aNodes 

  // Use the CSS class for formatting the invoice totals 
  // to locate the desired HTML elements. The CSS class must 
  // be known in advance. The HTML page has the following 
  // structure: 
  //  <div class="totals"> 
  //    <div class="totals-row subtotal"> 
  //       .. sub-total .. 
  //    </div> 
  //    <div class="totals-row tax"> 
  //       .. tax sum .. 
  //    </div> 
  //    <div class="totals-row total"> 
  //       .. total .. 
  //    </div> 
  //  </div> 
  // 
  // We'll use :queryAllByClass() to get all sums and extract 
  // the desired information. 
  aNodes := oViewer2:queryAllByClass( "totals-row" ):get() 
RETURN ExtractTotalsData(aNodes) 

// Extract the invoice totals data from an array of HTML 
// elements. The data in the elements is structured as follows: 
  //  <div class="totals-row .."> 
  //    <span>.. section heading ..</span> 
  //    <span>.. total ..</span> 
  //    </div> 
  //  </div> 
FUNCTION ExtractTotalsData( aNodes ) 
LOCAL oSpan 
LOCAL oText 
LOCAL i 
LOCAL oNode 
LOCAL cTotals := "" 

  FOR i:=1 TO Len(aNodes) 
     oNode := aNodes[i] 

     oSpan := oNode:children[1] 
     oText := oSpan:children[1] 
     cTotals += LTrim( oText:nodeValue ) 

     oSpan := oNode:children[2] 
     oText := oSpan:children[1] 
     cTotals += LTrim( oText:nodeValue ) + CRLF 
  NEXT i 
RETURN cTotals 

// Load an invoice given an invoice id 
FUNCTION LoadInvoice( cId ) 
LOCAL cHTML 

  // This function just returns static HTML code for demonstrational 
  // purpose. In a real-word application, the invoice would be loaded 
  // from disk or from a database 
  UNUSED( cId ) 

  TEXT INTO cHTML WRAP 
    <!DOCTYPE html> 
    <html lang="en"> 
    <head> 
        <meta charset="UTF-8"> 
        <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
        <title>Invoice #INV-2024-001</title> 
        <style> 
            * { 
                margin: 0; 
                padding: 0; 
                box-sizing: border-box; 
            } 
            body { 
                font-family: 'Arial', sans-serif; 
                color: #333; 
                background-color: #f5f5f5; 
                padding: 20px; 
            } 
            .invoice-container { 
                max-width: 210mm; 
                margin: 0 auto; 
                background: white; 
                padding: 20mm; 
                box-shadow: 0 0 10px rgba(0,0,0,0.1); 
            } 
            .header { 
                display: flex; 
                justify-content: space-between; 
                align-items: start; 
                margin-bottom: 20px; 
                padding-bottom: 12px; 
                border-bottom: 3px solid #2c3e50; 
            } 
            .company-details h1 { 
                font-size: 26px; 
                color: #2c3e50; 
                margin-bottom: 5px; 
            } 
            .company-details p { 
                color: #7f8c8d; 
                line-height: 1.4; 
                font-size: 13px; 
            } 
            .invoice-details { 
                text-align: right; 
            } 
            .invoice-details h2 { 
                font-size: 24px; 
                color: #e74c3c; 
                margin-bottom: 5px; 
            } 
            .invoice-details p { 
                line-height: 1.5; 
                font-size: 13px; 
            } 
            .addresses { 
                display: flex; 
                justify-content: space-between; 
                margin-bottom: 20px; 
            } 
            .address-block { 
                width: 48%; 
            } 
            .address-block h3 { 
                font-size: 12px; 
                text-transform: uppercase; 
                color: #7f8c8d; 
                margin-bottom: 6px; 
                letter-spacing: 1px; 
            } 
            .address-block p { 
                line-height: 1.5; 
                font-size: 13px; 
            } 
            table { 
                width: 100%; 
                border-collapse: collapse; 
                margin-bottom: 15px; 
                font-size: 13px; 
            } 
            thead { 
                background-color: #2c3e50; 
                color: white; 
            } 
            thead th { 
                padding: 10px 8px; 
                text-align: left; 
                font-weight: 600; 
                text-transform: uppercase; 
                font-size: 11px; 
                letter-spacing: 0.5px; 
            } 
            thead th:last-child { 
                text-align: right; 
            } 
            tbody td { 
                padding: 8px; 
                border-bottom: 1px solid #ecf0f1; 
                font-size: 13px; 
            } 
            tbody td small { 
                font-size: 11px; 
            } 
            tbody td:last-child { 
                text-align: right; 
                font-weight: 600; 
            } 
            tbody tr:hover { 
                background-color: #f8f9fa; 
            } 
            .totals { 
                margin-left: auto; 
                width: 280px; 
            } 
            .totals-row { 
                display: flex; 
                justify-content: space-between; 
                padding: 6px 0; 
                border-bottom: 1px solid #ecf0f1; 
                font-size: 13px; 
            } 
            .totals-row.subtotal { 
                font-size: 13px; 
            } 
            .totals-row.tax { 
                font-size: 13px; 
                color: #7f8c8d; 
            } 
            .totals-row.total { 
                font-size: 18px; 
                font-weight: bold; 
                color: #2c3e50; 
                border-top: 2px solid #2c3e50; 
                border-bottom: 3px double #2c3e50; 
                margin-top: 6px; 
                padding-top: 10px; 
            } 
            .notes { 
                margin-top: 20px; 
                padding: 12px; 
                background-color: #f8f9fa; 
                border-left: 4px solid #3498db; 
            } 
            .notes h3 { 
                font-size: 12px; 
                margin-bottom: 6px; 
                color: #2c3e50; 
            } 
            .notes p { 
                line-height: 1.4; 
                color: #7f8c8d; 
                font-size: 11px; 
            } 
            .footer { 
                margin-top: 20px; 
                padding-top: 12px; 
                border-top: 2px solid #ecf0f1; 
                text-align: center; 
                color: #95a5a6; 
                font-size: 10px; 
            } 
            /* Responsive styles for mobile and tablet */ 
            @media screen and (max-width: 768px) { 
                body { 
                    padding: 10px; 
                } 
                .invoice-container { 
                    padding: 15px; 
                } 
                .header { 
                    flex-direction: column; 
                    gap: 20px; 
                } 
                .company-details h1 { 
                    font-size: 24px; 
                } 
                .invoice-details { 
                    text-align: left; 
                } 
                .invoice-details h2 { 
                    font-size: 22px; 
                } 
                .addresses { 
                    flex-direction: column; 
                    gap: 20px; 
                } 
                .address-block { 
                    width: 100%; 
                } 
                table { 
                    font-size: 14px; 
                } 
                thead th { 
                    padding: 10px 5px; 
                    font-size: 11px; 
                } 
                tbody td { 
                    padding: 10px 5px; 
                } 
                tbody td small { 
                    font-size: 11px; 
                } 
                .totals { 
                    width: 100%; 
                } 
                .totals-row.total { 
                    font-size: 18px; 
                } 
                .notes { 
                    padding: 15px; 
                    font-size: 14px; 
                } 
            } 
            @media screen and (max-width: 480px) { 
                .company-details h1 { 
                    font-size: 20px; 
                } 
                .invoice-details h2 { 
                    font-size: 18px; 
                } 
                table { 
                    font-size: 12px; 
                } 
                thead th { 
                    padding: 8px 3px; 
                    font-size: 10px; 
                } 
                tbody td { 
                    padding: 8px 3px; 
                } 
                .totals-row { 
                    font-size: 14px; 
                } 
                .totals-row.total { 
                    font-size: 16px; 
                } 
            } 
            /* Print styles */ 
            @media print { 
                body { 
                    background: white; 
                    padding: 0; 
                } 
                .invoice-container { 
                    box-shadow: none; 
                    padding: 0; 
                    max-width: 100%; 
                } 
                @page { 
                    size: A4; 
                    margin: 20mm; 
                } 
                .no-print { 
                    display: none; 
                } 
            } 
        </style> 
    </head> 
    <body> 
        <div class="invoice-container"> 
            <!-- Header --> 
            <div class="header"> 
                <div class="company-details"> 
                    <h1>ACME Corp</h1> 
                    <p> 
                        123 Business Street<br> 
                        New York, NY 10001<br> 
                        Phone: (555) 123-4567<br> 
                        Email: billing@acmecorp.com 
                    </p> 
                </div> 
                <div class="invoice-details"> 
                    <h2>INVOICE</h2> 
                    <p> 
                        <strong>Invoice #:</strong> INV-2024-001<br> 
                        <strong>Date:</strong> October 28, 2025<br> 
                        <strong>Due Date:</strong> November 27, 2025 
                    </p> 
                </div> 
            </div> 
            <!-- Addresses --> 
            <div class="addresses"> 
                <div class="address-block"> 
                    <h3>Bill To</h3> 
                    <p> 
                        <strong>John Smith</strong><br> 
                        XYZ Corporation<br> 
                        456 Client Avenue<br> 
                        Los Angeles, CA 90001<br> 
                        United States 
                    </p> 
                </div> 
                <div class="address-block"> 
                    <h3>Ship To</h3> 
                    <p> 
                        <strong>John Smith</strong><br> 
                        XYZ Corporation<br> 
                        789 Delivery Road<br> 
                        Los Angeles, CA 90002<br> 
                        United States 
                    </p> 
                </div> 
            </div> 
            <!-- Items Table --> 
            <table> 
                <thead> 
                    <tr> 
                        <th>Description</th> 
                        <th>Quantity</th> 
                        <th>Unit Price</th> 
                        <th>Amount</th> 
                    </tr> 
                </thead> 
                <tbody> 
                    <tr> 
                        <td> 
                            <strong>Website Design & Development</strong><br> 
                            <small>Custom responsive website with 5 pages</small> 
                        </td> 
                        <td>1</td> 
                        <td>$2,500.00</td> 
                        <td>$2,500.00</td> 
                    </tr> 
                    <tr> 
                        <td> 
                            <strong>Logo Design</strong><br> 
                            <small>Professional logo with 3 revision rounds</small> 
                        </td> 
                        <td>1</td> 
                        <td>$750.00</td> 
                        <td>$750.00</td> 
                    </tr> 
                    <tr> 
                        <td> 
                            <strong>SEO Optimization</strong><br> 
                            <small>On-page SEO and meta tags setup</small> 
                        </td> 
                        <td>1</td> 
                        <td>$500.00</td> 
                        <td>$500.00</td> 
                    </tr> 
                    <tr> 
                        <td> 
                            <strong>Content Management System</strong><br> 
                            <small>CMS integration and training</small> 
                        </td> 
                        <td>1</td> 
                        <td>$1,200.00</td> 
                        <td>$1,200.00</td> 
                    </tr> 
                    <tr> 
                        <td> 
                            <strong>Hosting & Domain (1 Year)</strong><br> 
                            <small>Premium hosting and domain registration</small> 
                        </td> 
                        <td>1</td> 
                        <td>$350.00</td> 
                        <td>$350.00</td> 
                    </tr> 
                </tbody> 
            </table> 
            <!-- Totals --> 
            <div class="totals"> 
                <div class="totals-row subtotal"> 
                    <span>Subtotal:</span> 
                    <span>$5,300.00</span> 
                </div> 
                <div class="totals-row tax"> 
                    <span>Tax (10%):</span> 
                    <span>$530.00</span> 
                </div> 
                <div class="totals-row total"> 
                    <span>Total:</span> 
                    <span>$5,830.00</span> 
                </div> 
            </div> 
            <!-- Notes --> 
            <div class="notes"> 
                <h3>Payment Terms & Notes</h3> 
                <p> 
                    Payment is due within 30 days of invoice date. Please make payment via bank transfer to the account details below, 
                    or by check made payable to ACME Corp. Late payments may incur a 5% monthly interest charge. 
                    Thank you for your business! 
                </p> 
                <p style="margin-top: 10px;"> 
                    <strong>Bank Details:</strong> Bank of America | Account: 123456789 | Routing: 987654321 
                </p> 
            </div> 
            <!-- Footer --> 
            <div class="footer"> 
                <p>Thank you for your business!</p> 
                <p>ACME Corp • Tax ID: 12-3456789 • www.acmecorp.com</p> 
            </div> 
        </div> 
    </body> 
    </html> 
  ENDTEXT 
RETURN cHTML 

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.