ediblecode
ediblecode

Reputation: 11971

Querying an XML file using HTML

I have an .xml file which has an accompanying .xsl file. The output of which can be found here.

I am attempting to have a html webpage where the user will be able to type into a search box, for example 'Year < 2009'. And it will show the table found above but only where the Year is less than 2009.

I don't really have much idea where to start, or what I need to look into to create this functionality.

For reference, here's an XML snippet:

<?xml-stylesheet type="text/xsl" href="podcatalog.xsl"?>
<pods-papers>
  <inproceedings key="AbbadiSC85">
    <crossref>conf/pods/85</crossref>
    <author>Amr El Abbadi</author>,
    <author>Dale Skeen</author>,
    <author>Flaviu Cristian</author>,
    <title>An Efficient, Fault-Tolerant Protocol for Replicated Data Management.</title>,
    <pages>215-229</pages>,
    <year>1985</year>,
    <booktitle>PODS</booktitle>,
  </inproceedings>
  <inproceedings key="AbbadiT86">
    <crossref>conf/pods/86</crossref>
    <author>Amr El Abbadi</author>,
    <author>Sam Toueg</author>,
    <title>Availability in Partitioned Replicated Databases.</title>,
    <pages>240-251</pages>,
    <year>1986</year>,
    <booktitle>PODS</booktitle>,
  </inproceedings>
  <inproceedings key="Abdel-GhaffarA90">
    <crossref>conf/pods/90</crossref>
    <author>Khaled A. S. Abdel-Ghaffar</author>,
    <author>Amr El Abbadi</author>,
    <title>On the Optimality of Disk Allocation for Cartesian Product Files.</title>,
    <pages>258-264</pages>,
    <year>1990</year>,
    <booktitle>PODS</booktitle>,
  </inproceedings>
  <inproceedings key="Abiteboul83">
    <crossref>conf/pods/83</crossref>
    <author>Serge Abiteboul</author>,
    <title>Disaggregations in Databases.</title>,
    <pages>384-388</pages>,
    <year>1983</year>,
    <booktitle>PODS</booktitle>,
  </inproceedings>
</pods-papers>

and here's the XSLT ("podcatalog.xsl"):

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
  <body>
  <h2>Pods Papers</h2>
  <table border="1">
    <tr bgcolor="#9ACD32">
      <th>Author</th>
      <th>Title</th>
      <th>Pages</th>
      <th>Year</th>
    </tr>
    <xsl:for-each select="pods-papers/inproceedings">
    <xsl:sort select="author"/>
    <tr>
      <td><xsl:value-of select="author"/></td>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="pages"/></td>
      <td><xsl:value-of select="year"/></td>
    </tr>
    </xsl:for-each>
  </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

Please note: I am not necessarily looking for the code, or what to write, but, for example, a web page that shows me how to implement all or parts of this

Upvotes: 1

Views: 619

Answers (1)

Tomalak
Tomalak

Reputation: 338128

You could do this:

Add two script elements to your XSLT that will show up in the resulting HTML. The first will be jQuery (the Google-hosted version, for convenience):

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

and the second manipulates your result table:

<script type="text/javascript"><![CDATA[
$(function () {
  var filters = {};
  filters.run = function () {
    var i, $th = $("th"), $td, value;

    $("table tr:not(:visible)").show(); // clear any previously hidden rows

    for (var i=0; i<$th.length; i++) {  // for each filter string...
      if (this[i] > "") {
        $td = $th.eq(i).closest("table").find("tr:visible td:nth-child(" + (i+1) + ")");

        switch (this[i].slice(0,1)) { // first character is the operator
          case "<":
            value = $.trim( this[i].slice(1).toLowerCase() );
            $td.filter(function () {
              return $.trim( $(this).text().toLowerCase() ) >= value;
            }).closest("tr:visible").hide();
            break;
          case ">":
            value = $.trim( this[i].slice(1).toLowerCase() );
            $td.filter(function () {
              return $.trim( $(this).text().toLowerCase() ) <= value;
            }).closest("tr:visible").hide();
            break;
          case "/":
            value = new RegExp( this[i].slice(1) );
            $td.filter(function () {
              return !value.test( $.trim( $(this).text() ) );
            }).closest("tr:visible").hide();
            break;
          case "=":
            value = $.trim( this[i].slice(1).toLowerCase() );
            $td.filter(function () {
              return $.trim( $(this).text().toLowerCase() ) != value;
            }).closest("tr:visible").hide();
            break;
          default:
            value = $.trim( this[i].toLowerCase() );
            $td.filter(function () {
              return $.trim( $(this).text().toLowerCase() ).indexOf(value) == -1;
            }).closest("tr:visible").hide();
            break;
        }
      }
    }
  }

  $("th").each(function () {
    $(this).append("<input type='text'>");
  });

  $("th input").change(function () {
    var index = $(this).closest("th").prevAll("th").length;

    filters[index] = $.trim( $(this).val() );
    filters.run();
  });
});
]]></script>

Here's what this does:

  1. It adds an <input> element to each <th>. This input serves as a filter box for that column.
  2. It maintains a set of filters, one for each column index.
  3. Whenever an input changes (the change event occurs when the field loses focus), it writes the field's value to the set of filters and runs the filter.
  4. "Running the filter" means:
    1. It goes through each saved filter string, figures out which operator (if any) was used
    2. It finds all <td> elements in the affected column and compares them to the filter value.
    3. When the cell value does not match the filter, the associated <tr> is hidden.
    4. Repeat process for next column.

This process results in additive filters, i.e. you can narrow down your search, filtering each column individually.

The first character of the filter string is supposed to be the operator.

Supported operators are

  • < (less than),
  • > (greater than)
  • = (exact equality)
  • / (regular expression).
  • Any other filter string is interpreted as "contains".
  • Add more operators if you want.

All comparisons except regular expression are case-insensitive.

See it in action on jsFiddle: http://jsfiddle.net/Tomalak/wjDrr/1/embedded/result/


P.S.:

I know this basically solves your entire problem. You said you have no idea where to start, so I suppose you'll have a hard-enough time figuring out the code and further adapting it to your needs. :)

The above code lacks some elegance, it breaks as soon as there is more then one table on the page and it certainly can be optimized for speed. If you care, you can improve these things.

Upvotes: 1

Related Questions