Search code examples
jqueryhtmlhtml-tableparentreplacewith

Replace only top-level parent table elements with divs, not child tables


I am looking to replace a table and it's rows and cells with divs, but the script I have found replaces every child table and their descendents with divs too, which I do not want. Child tables should remain tables.

How do I just target the top-level parent table and not any of the children tables?

    $('table.calendar-header').each(function (){
        $(this).replaceWith( $(this).html()
            .replace(/<tbody/gi, "<div class='table'")
            .replace(/<tr/gi, "<div class='tr'")
            .replace(/<\/tr>/gi, "</div>")
            .replace(/<td/gi, "<div class='td")
            .replace(/<\/td>/gi, "</div>")
            .replace(/<\/tbody/gi, "<\/div")
        );
    });

I've tried inserting :first-child and .first() into the script in various places to no avail.

Here's what the HTML looks like before script runs [note that I left out content from child tables in this code snippet]. You can see that there are tables within the parent table. I want those to remain as is, not be replaced.

<table class="calendar-header" cellspacing="0" cellpadding="6" border="0" style="">
    <tbody>
        <tr>
            <td valign="top">
                <table class="calendar" cellspacing="0" cellpadding="2"></table>
            </td>
            <td align="center">
                <div class="eventNav"></div>
            </td>
            <td valign="top" align="right">
                <table class="calendar" cellspacing="0" cellpadding="2"></table>
            </td>
        </tr>
    </tbody>
</table>

Solution

  • I was able to do it like shown below.

    In order for this to work, your child tables cannot have a class calendar-header. If they do, you'll have to change the check if ($(obj).closest("table").hasClass("calendar-header")) to something unique to your parent table.

    As a side note, just be aware that if your user is using Firefox and AdBlockPlus, any div with class thead will be mistaken by theAd ("the advertisement"), and your div will be hidden by the plugin (it will dynamically add a link to elemhide.css). So I changed the resulting classes names to divTable, divThead, etc:

    $('table.calendar-header').each(function (){
        // It's important to go from farthest tag to the nearest.
        $(this).find("th").each(replacer);
        $(this).find("td").each(replacer);
        $(this).find("tr").each(replacer);
        $(this).find("tbody").each(replacer);
        $(this).find("thead").each(replacer);
        replacer(0, this); // replace the table tag
    });
    
    function replacer(idx, obj) {
        tagName = capitalizeFirstLetter($(obj).prop("tagName").toLowerCase());
        if ($(obj).closest("table").hasClass("calendar-header")) {
            $(obj).replaceWith("<div class='div" + tagName + "'>" + obj.innerHTML + '</div>');
        }
    }
    
    function capitalizeFirstLetter(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    }