使用 header 单元格文本定位 table 单元格

Locating table cell using the header cell text

我有 table 种外观,如下图所示,但不是单一的 table。 Header 在一个 table 中,行在另一个 table 中。

header 有一个 table 主要、语言和添加按钮,其余两行在另一个 table 中。现在我必须使用 header 文本来识别单元格。例如,如果我给出 1 Language 那么它必须找到选择阿拉伯语的第一行第二个单元格。同样,如果我给 2 Primary 它找到第二行第一列。

HTML代码如下图所示。如果可以解决这个问题,那么我会给出实际代码。

<div id="d78Pt30" style="width: 35em;" class="gridMaxHeight z-grid">
    <div id="d78Pt30-head" class="z-grid-header" style="">
        <table id="d78Pt30-headtbl" style="table-layout: fixed;" width="100%">
            <colgroup id="d78Px30-hdfaker">
                <col id="d78Py30-hdfaker" style="width: 61px;">
                <col id="d78Pz30-hdfaker" style="">
                <col id="d78P_40-hdfaker" style="width: 50px;">
                <col id="d78Px30-hdfaker-bar" style="width: 0px">
            </colgroup>
            <tbody id="d78Pt30-headrows">
            <tr id="d78Px30" class="z-columns" style="text-align: left;">
                <th id="d78Py30" class="z-column">
                    <div id="d78Py30-cave" class="z-column-content">
                        <div class="z-column-sorticon"><i id="d78Py30-sort-icon"></i></div>
                        Primary
                    </div>
                </th>
                <th id="d78Pz30" class="z-column">
                    <div id="d78Pz30-cave" class="z-column-content">
                        <div class="z-column-sorticon"><i id="d78Pz30-sort-icon"></i></div>
                        Language
                    </div>
                </th>
                <th id="d78P_40" class="z-column">
                    <div id="d78P_40-cave" class="z-column-content">
                        <div class="z-column-sorticon"><i id="d78P_40-sort-icon"></i></div>
                        <a id="d78P040" class="z-a" href="javascript:;"><img src="assets/images/add.png"
                                                                             align="absmiddle"></a></div>
                </th>
                <th id="d78Px30-bar" class="z-columns-bar"></th>
            </tr>
            </tbody>
        </table>
    </div>
    <div class="z-grid-header-border"></div>
    <div id="d78Pt30-body" class="z-grid-body" style="overflow: auto;">
        <table id="d78Pt30-cave" style="table-layout: fixed;" width="100%">
            <colgroup id="d78Px30-bdfaker">
                <col id="d78Py30-bdfaker" style="width: 61px;">
                <col id="d78Pz30-bdfaker" style="">
                <col id="d78P_40-bdfaker" style="width: 50px;">
            </colgroup>
            <tbody id="d78Pi50" class="z-rows">
            <tr id="d78P260" class="gridMaxHeight z-row">
                <td id="d78P360-chdextr" class="z-row-inner">
                    <div id="d78P360-cell" class="z-row-content"><span id="d78P360"
                                                                       class="z-radio z-radio-default"><input
                            type="radio" id="d78P360-real" name="d78P360" checked="checked"><label for="d78P360-real"
                                                                                                   id="d78P360-cnt"
                                                                                                   class="z-radio-content"></label></span>
                    </div>
                </td>
                <td id="d78P460-chdextr" class="z-row-inner">
                    <div id="d78P460-cell" class="z-row-content"><span id="d78P460" class="z-combobox"
                                                                       style="width: 225px;"><input id="d78P460-real"
                                                                                                    class="z-combobox-input"
                                                                                                    autocomplete="off"
                                                                                                    value="" type="text"
                                                                                                    size="20"
                                                                                                    style="width: 196px;"><a
                            id="d78P460-btn" class="z-combobox-button"><i id="d78P460-icon"
                                                                          class="z-combobox-icon z-icon-caret-down"></i></a><div
                            id="d78P460-pp" style="display: none;"></div></span></div>
                </td>
                <td id="d78Py60-chdextr" class="z-row-inner">
                    <div id="d78Py60-cell" class="z-row-content">
                        <div id="d78Py60" class="z-hlayout">
                            <div id="d78Pz60-chdex" class="z-hlayout-inner" style=""><a id="d78Pz60" class="z-a"
                                                                                        href="javascript:;"><img
                                    src="assets/images/delete.png" align="absmiddle"></a></div>
                        </div>
                    </div>
                </td>
            </tr>
            </tbody>
            <tbody class="z-grid-emptybody">
            <tr>
                <td id="d78Pt30-empty" style="display: none;" colspan="3">
                    <div id="d78Pt30-empty-content" class="z-grid-emptybody-content">No data available</div>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

如您在 HTML 中所见,有两个 table,第一个包含 header,第二个包含其余行。

由于未提供 table 的完整标记,我在答案末尾使用 HTML 来说明可能的解决方案。

解决方案 1 - 对列索引进行硬编码

如果 table 列是 _static,最简单的解决方案是在您的代码中硬编码索引查找。此解决方案还可以更轻松地处理 header 没有文本的单元格 - 例如添加图标。

例如,您知道“语言”header 始终是列索引 1,而“主要”始终是列索引 0。因此,您知道如果需要数据行的“语言” , 它将是行中的第二个单元格。

def cell_by_header_text(browser, data_row_index, header_text)
  columns = ['Primary', 'Language', 'Add'] # must match the order on the page
  column_index = columns.index(header_text)

  data_table = browser.div(class: 'z-grid-body').table
  data_table[data_row_index - 1][column_index] # returns Watir::Cell
end

p cell_by_header_text(browser, 1, 'Language').html
#=> "<td><select><option selected=\"selected\">Arabic</option><option>Bengali</option></select></td>"
p cell_by_header_text(browser, 2, 'Primary').html
#=> "<td><input type=\"radio\" checked=\"\"></td>"

解决方案 2 - 列索引的动态查找

如果 table 列是 动态的 或者您想要更通用的解决方案,您可以从 header table.

def cell_by_header_text(browser, data_row_index, header_text)
  header_table = browser.div(class: 'z-grid-header').table
  column_index = header_table.tds.find_index { |td| td.text == header_text }

  data_table = browser.div(class: 'z-grid-body').table
  data_table[data_row_index - 1][column_index] # returns Watir::Cell
end

解决方案 3 - Domain-specific collection

如果你想提高可读性和灵活性,你可以更进一步,为网格创建一个domain-specific collection:

class LanguageRowCollection
  include Enumerable

  def initialize(browser)
    @browser = browser
  end

  def each
    data_rows.map { |data| yield LanguageRow.new(header_row, data) }
  end

  def [](value)
    to_a[value]
  end

  private

  def header_row
    @browser.div(class: 'z-grid-header').table.tr
  end

  def data_rows
    @browser.div(class: 'z-grid-body').table.trs
  end
end

class LanguageRow
  def initialize(header_row, tr)
    @header_row = header_row
    @tr = tr
  end

  def primary_cell
    @tr.tds[@header_row.tds.map(&:text).index('Primary')]
  end

  def primary?
    primary_cell.radio.selected?
  end

  def set_primary(value)
    primary_cell.radio.set(value)
  end

  def language_cell
    @tr.tds[@header_row.tds.map(&:text).index('Language')]
  end

  def language
    language_cell.select.text
  end

  def set_language(value)
    language_cell.select.set(value)
  end

  def remove_cell
    # Locating the 3rd column by it's image since it doesn't have text
    @tr.tds[@header_row.tds.find_index { |td| td.image(class: 'add').exists? }]
  end

  def remove
    remove_cell.link.click
  end
end

def languages(browser)
  grid = browser.div(class: 'z-grid')
  LanguageRowCollection.new(grid)
end

您可以获得 get/set 值的更易读的方式:

# Get/set the language of the first row (note the 0-based index)
languages(browser)[0].language
#=> "Arabic"
languages(browser)[0].set_language('Bengali')

您还可以根据值灵活地定位行:

# Get the primary language
languages(browser).find(&:primary?).language
#=> "Bengali"

# Remove the Arabic row
languages(browser).find { |l| l.language == 'Arabic' }.remove

HTML 例子

以下HTML用于上述示例。

<html>
  <body>
    <div id="d78Pt30" class="gridMaxHeight -grid">
      <div class="z-grid-header">
        <table>
          <tr>
            <td>Primary</td>
            <td>Language</td>
            <td>Add</td>
          </tr>
        </table>
      </div>
      <div class="z-grid-header-border"></div>
      <div class="z-grid-body">
        <table>
          <tr>
            <td><input type="radio"></td>
            <td><select><option selected="selected">Arabic</option><option>Bengali</option></select></td>
            <td><a href="#">Minus</a></td>
          </tr>
          <tr>
            <td><input type="radio" checked></td>
            <td><select><option>Arabic</option><option selected="selected">Bengali</option></select></td>
            <td><a href="#">Minus</a></td>
          </tr>
      </div>
    </div>
  </body>
</html>