Find <tr> Element of a certain content

Asked

Viewed 1,689 times

10

I would like to know how to find a certain element <tr> in a numbered table. For example: In a table where each row <tr> has its cells <td> numbered 1-5, containing 4 lines <tr>, if I pass number 15, I get as a result the 3rd line <tr>, if I pass number 17, I get as a result the 4th line <tr>, and so on.

With table.match(/<tr>/g), I get all 4 elements <tr>, but what I want is to get just one element <tr>, as a final result, according to the numerical criterion explained above.

The goal is to apply a style to the tag <tr>, an outline, outline: solid 1px red;, to highlight.

var table = ['<table>
<tr>
<td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>
</tr>
<tr>
<td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>
</tr>
<tr>
<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td>
</tr>
<tr>
<td>16</td><td>17</td><td>18</td><td>19</td><td>20</td>
</tr>
</table>'];

table.match(/<tr>/g); // Me devolve todos os elementos <tr>

The table is originally the function below that generates a Calendar. I was able to highlight the present day. Now the challenge is do the same with the current week:

function Calendar(id, year, month) { 
  var elem = document.getElementById(id)

  var mon = month - 1
  var d = new Date(year, mon)
  var e = new Date();
  var weekDay = ["Sun", "Mon", "Tues", "Wed", "Thu", "Fri", "Sat"];
  var months = ['January', 'Frebruary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
  var table = ['<table><tr>']

  table.push('<th colspan = 4>' + months[mon] + '</th>' + '<th colspan = 3>' + year + '</th>' + '</tr><tr>')
  for (var i = 0; i <= 6; i++ ) {
    table.push('<th>' + weekDay[i] + '</th>')
  }
  table.push('</tr><tr>')

  for (var i=0; i<d.getDay(); i++) {
    table.push('<td></td>')
  }

  while(d.getMonth() == mon) {

    if (d.getDate() == e.getDate()) { // Condicional para dar destaque ao dia atual
    table.push('<td class=day>'+d.getDate()+'</td>')
    } else {
    table.push('<td>'+d.getDate()+'</td>')
    }

    if (d.getDay() % 7 == 6) {
      table.push('</tr><tr>')
    }

    d.setDate(d.getDate()+1)  
  }

  for (var i=d.getDay(); i<7; i++) {
    table.push('<td></td>')
  }

  table.push('</tr></table>')

  elem.innerHTML = table.join('\n')
}

new Calendar("cal", 2015, 3);
  • 1

    This HTML does not have enough distinct marks for use of regex, I think a solution that parses the fragment will suit you better.

2 answers

10

EDIT

If the table structure doesn’t really change:

  • no container tables inside tables
  • the HTML is well formed

then it is possible to use regex to find the opening tag of the TR that contains the TD with the desired number, as shown in the snippet below:

var num = 8;

var tabela = getTable();

var tabelaSubst = tabela.replace(RegExp("\\<\\s*tr\\s*\\>(?=((?!\\<\\s*\\/tr\\s*\\>).)*\\D\\s*0*"+num+"\\s*\\D)", 'g'), "<TR class='achou'>");

document.getElementById("content").innerHTML = (tabelaSubst);

function encodeHtml(rawStr) {
    return rawStr && rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
        return '&#' + i.charCodeAt(0) + ';';
    });
}

function getTable() {
    var table = ['<table>' +
        '<tr>' +
        '<td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>' +
        '</tr>' +
        '<tr>' +
        '<td>6</td><td>7</td><td> 08</td><td>9</td><td>10</td>' +
        '</tr>' +
        '<tr>' +
        '<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td>' +
        '</tr>' +
        '<tr>' +
        '<td>16</td><td>17</td><td>18</td><td>19</td><td>20</td>' +
        '</tr>' +
        '</table>'
    ];
    return table[0];
}
.achou {
     color: red;
}
<div id="content"></div>

    jsfiddle

 

OLD: using XML parser

You can use the broswer XML parse Apis, and use Xpath for this.

What is this API?

  • DOMParser: allows parsing XML documents (well formed HTML is XML), and creating an AST, which contains the document structure

  • Document evaluate.: allows searching using Xpath in an XML document, which has been obtained by DOMParser, or even the loaded document, that is, the DOM of the loaded page

To support IE8

IE8 does not support the above API’s directly... but it is possible to use your own:

  • ActiveXObject("Microsoft.XMLDOM") allows parsing XML, in addition to applying Xpath, all using a single object

Xpath

This is the standard language for making queries in XML documents. But don’t get discouraged by the word Path because Xpath is far from being a simple path selector.

Example

function getTrsModernBrowser() {
    var parser = new DOMParser();
    var frag = parser.parseFromString(getTable()[0], "text/xml");
    var xpathResult = frag.evaluate("//tr[td[text()=17]]", frag, null, XPathResult.ANY_TYPE, null);

    var trs = [];
    var result = xpathResult.iterateNext();
    while (result) {
        trs.push(result);
        result = xpathResult.iterateNext();
    }
    return trs;
}

function getTrsIE8() {
    var frag = new window.ActiveXObject("Microsoft.XMLDOM");
    frag.async = "false";
    frag.loadXML(getTable()[0]);
    frag.setProperty("SelectionLanguage", "XPath");
    var results = frag.selectNodes("//tr[td[text()=17]]");
    var trs = [];
    for (it = 0; it < results.length; it++)
        trs.push(results[it]);
    debugger;
    return trs;
}

function getTrs() {
    return window.ActiveXObject ? getTrsIE8() : getTrsModernBrowser();
}

var trs = getTrs();
for (var itTr = 0; itTr < trs.length; itTr++) {
    document.getElementById("content").innerHTML = encodeHtml(
        trs[itTr].outerHTML // browsers que suportam a API DOMParser
        || trs[itTr].xml // IE8
    );
}

function encodeHtml(rawStr) {
    return rawStr && rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
        return '&#' + i.charCodeAt(0) + ';';
    });
}

function getTable() {
    var table = ['<table>' +
        '<tr>' +
        '<td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>' +
        '</tr>' +
        '<tr>' +
        '<td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>' +
        '</tr>' +
        '<tr>' +
        '<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td>' +
        '</tr>' +
        '<tr>' +
        '<td>16</td><td>17</td><td>18</td><td>19</td><td>20</td>' +
        '</tr>' +
        '</table>'
    ];
    return table;
}
<div id="content"></div>

    jsfiddle, why do you like playing there instead of the SOPT snippet

Dissecting the Xpath above the example

  • //tr[td[text()=17]]

    1. // at first instruct the mechanism to seek nodes at all levels, and not only at the root
    2. tr is the element to be found, and could be the name of any element
    3. [ condição ] this operator is an assertion about the previous element, only will be selected trs with the characteristics defined within the operator []
    4. text()=17 indicates that the text of the element being analyzed should be the number 17. Watch what I said: "THE NUMBER 17"... numeric comparator will be used. So the widget text can be "017", " 00017 ", "17", etc.
  • 1

    Fiddle! Fiddle! Fiddle! : ) Curious, the applications are more than many!

  • Calm down! I’ll add information... I’m in the process of discovery yet. =)

  • @Zuul Now the answer is more consistent. Tell me what you thought... if it’s good, if I’m missing any more points... etc.

  • 1

    @Zuul Fixed! Instead of document.evaluate should have used frag.evaluate.

  • @Miguelangelo - Thought I wasn’t very clear on my question. Your code returns all cells belonging to tag <tr>. What I need is more or less the other way around. I want the tag <tr> which contains the number passed, not the series of cells within it. If there is a simpler way to accomplish this, with regex, better.

  • The return is not the Tds... I only print the Tds from the property innerHTML of the TR itself found.

  • @Eden I changed the example to print the TR itself... anything you want to do with TR is possible, but you have to tell me exactly what you are trying to do.

  • @Miguelangelo - I want to apply a style to the whole line (outline: solid 1px red;). The reason for a simpler code, preferably in regex, is that the software in which the code will run has somewhat precarious support for Javascript. Any more complex code tends not to work. The table will invariably have the same format, only c/numbers, and all I need is simply the tag <tr> opening, nothing more, not even need the closing tag.

  • @So you’re actually hoping to make a replace using regex? Man, you can do it using regex, but will only work if the table structure changes for nothing in this world. =) I’ll set one up here and post.

  • @Eden Com regex also can not have table inside table, if you have already been... there is no way.

  • @Miguelangelo - The table is originally a Calendar generated by a function I obtained on this website - "Create a Function to create a Calendar table for Given Year/Month". The goal is to actually make a replace() with the regex.

  • Ready! I edited the answer with a regex to do the replace. Soon I explain what is going on in this regex... I will have to leave a moment.

Show 7 more comments

8


I suggest doing this via HTML in Browser which is much better than Regex to read HTML.
You don’t need to join the DOM but using the Browser is safer.

Suggestion:

Join this HTML to a DIV:

var div = document.createElement('div');
div.innerHTML = table;

Join the arrays method to the nodeLists methods for use querySelectorAll:

NodeList.prototype.filter = Array.prototype.filter;

Use the methods you use in the DOM on that div:

var value = input.value || 0;
var tds = div.querySelectorAll('td');
var match = tds.filter(function (td) {
    return td.innerHTML == value;
});
var resultado = match && match[0].parentNode;
alert(resultado.innerHTML); // vai mostrar só a linha que queres

example: http://jsfiddle.net/zgbctL0o/

EDIT:

Following the clarifications added to the question the final result with my code was to add at the end of this function these lines:

// mark this week
var today = (new Date()).getDate();
var tds = elem.querySelectorAll('td');
NodeList.prototype.filter = Array.prototype.filter;
var match = tds.filter(function (td) {
    return td.innerHTML == today;
});
if (match) match[0].parentNode.style.outline = 'solid 1px red';

jsFiddle: http://jsfiddle.net/efuu16b8/

  • You thought I was unclear on my question. Your code returns all cells belonging to the tag <tr>, same as Miguel Angelo. What I need is more or less the other way around. I want the tag <tr> which contains the number passed, not the series of cells within it. If there is a simpler way to accomplish this, with regex, better.

  • @I give you my code tr. Only in Alert did I gather the content to show that it was the tr right. The variable resultado is the tr. Regex element is no safer than this way I put in the answer.

  • With Regexp I would be able to make one replace sort of like this: replace([<tr> desejada], '<tr style=outline=solid 1px red'), applying an outline to the desired tag. How to accomplish this with your solution?

  • @Eden then you can use resultado.style.outline = 'solid 1px red'; How will you use this table? is already in the DOM? if yes is still easier... or you have it in string and want to keep it in string? explains better that together example also.

  • The table is originally a Calendar generated by a function I obtained on this website - "Create a Function to create a Calendar table for Given Year/Month". It is to apply the style during or after table generation, still within the function. Using a conditional, I was able to apply table.push('<td class=day>'+d.getDate()+'</td>') during the generation of the table. What I want now is to highlight the <tr> which represents the current week. I will update the question to insert the Calendar function.

  • 1

    @Eden if you had placed this information from the beginning you had a response with direct example :) Here is an example using my solution: http://jsfiddle.net/qmLjmqsL/

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.