How to filter and display a row when the search input matches the content within a collapsed in DataTables?

I’m using DataTables to manage a table where certain contents are in collapsible <tr> elements. Initially, these <tr> are hidden (display: none), and they only become visible when their corresponding button (Details) is pressed .

When using the DataTables search filter, I’d like the table to display the main row that contains the matching content that is in the hidden <tr> element, without requiring the user to click the “Details” button first.

Here is my current setup:

Each collapsible row has a unique ID, and I store the content of each row in a dictionary with the following structure:
{ ‘id_of_row’: [‘content1’, ‘content2’, ‘content3’, …] }
The goal is to search within the dictionary when the user types in the search box, and if a match is found in any of the dictionary elements, it uses the id_of_row and the corresponding main row with that id should be displayed.

Example Problem:

If I search for the term ‘requ’, nothing shows up because the corresponding content is inside a collapsed <tr>. However, if I manually expand the <tr> by clicking the “Details” button and make that content visible and if there is a match inside that content it will show up.

What I’ve Tried:

I use a dictionary that maps the main row’s ID to an array of content for that row. When the search input matches an element in the dictionary, I want the corresponding main row to be displayed, even if it’s content its inside a currently hidden element.
However, I can’t seem to get this to work as expected. The row is not being displayed when its content matches the search term unless it’s manually expanded.

Here is a simplified version of my current code:

@using Newtonsoft.Json
@{
// Sample machine names
var MachineNames = new List<string> { "Machine A", "Machine B", "Machine C" };

// Sample requests using anonymous objects
var requests = new[]
{
new { Id = 1, ContactName = "Client A", CompanyName = "Machine A", Status = "Pending", Msg = "Request 1" },
new { Id = 2, ContactName = "Client B", CompanyName = "Machine B", Status = "In Progress", Msg = "Request 2" },
new { Id = 3, ContactName = "Client C", CompanyName = "Machine C", Status = "Completed", Msg = "Request 3" }
};

// Sample dictionary data for matching the behavior of your original example
var requestDictionary = new Dictionary<int, string[]>
{
{ 1, new[] { "client a", "machine a", "pending", "request 1" } },
{ 2, new[] { "client b", "machine b", "in progress", "request 2" } },
{ 3, new[] { "client c", "machine c", "completed", "request 3" } }
};
}

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Collapsible Table with Custom Filter</title>

    <!-- Include jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

    <!-- Include DataTables CSS & JS -->
    <link rel="stylesheet" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css">
    <script src="https://cdn.datatables.net/1.10.5/js/jquery.dataTables.min.js"></script>

    <style>
        table {
            width: 100%;
            border-collapse: collapse;
        }

        th, td {
            padding: 8px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }

        .show {
            display: table-row;
        }

        .dataTables_filter label {
            font-weight: bold;
            font-size: 1.2em;
        }

        .dataTables_filter input {
            margin-left: 10px;
            padding: 5px;
            border-radius: 5px;
            border: 1px solid #ccc;
        }

        #search-container {
            width: auto;
            padding: 5px;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
<h1>Collapsible Table with Custom Search Filter</h1>

<!-- Search filter -->
<div id="search-container"></div>

<!-- The table with collapsible rows -->
<table id="myTable" class="display table-flexible">
    <thead>
    <tr>
        <th>Request ID</th>
        <th>Client</th>
        <th>Machine</th>
        <th>Status</th>
        <th>Description</th>
    </tr>
    </thead>
    <tbody>
    @foreach (var request in requests)
    {
    <tr>
        <td>@request.Id</td>
        <td>@request.ContactName</td>
        <td data-id="@request.CompanyName">@request.CompanyName</td>
        <td>@request.Status</td>
        <td>
            <button class="btn btn-info btn-sm toggle-btn" data-target="#[email protected]">Details</button>
        </td>
    </tr>

    <tr id="[email protected]" class="details-row" style="display: none;">
        <td colspan="6">
            <div class="card custom-card-body details-card">
                <div class="detail-item">
                    <strong>Message:</strong>
                    <p class="tw-font-bold">@request.Msg</p>
                </div>
            </div>
        </td>
    </tr>
    }
    </tbody>
</table>

@section Scripts {
<script src="https://cdn.datatables.net/1.10.5/js/jquery.dataTables.min.js"></script>
<script>
    $(document).ready(function () {
       
        var requestDictionary = @Html.Raw(JsonConvert.SerializeObject(requestDictionary));

        console.log(requestDictionary); // Debugging purposes
        
        // Initialize DataTable
        var table = $('#myTable').DataTable({
            columnDefs: [{
                "defaultContent": "-",
                "targets": "_all"
            }],
            paging: false,
            ordering: false,
            info: false,
            dom: 'ft'
        });

        // Move search box to custom container
        $('#search-container').html($('.dataTables_filter'));

        // Custom search functionality using the requestDictionary
        function filterTable(searchTerm) {
            var rows = table.rows().nodes(); // Fetch all rows
            $(rows).each(function () {
                var $row = $(this); // The `tr` element

                // Extract requestId from the first column (adjust if necessary)
                var requestId = $row.find('td').first().text().trim();

                // Ensure requestId is a valid number
                requestId = parseInt(requestId, 10);
                if (isNaN(requestId)) {
                    return; // Skip rows without a valid requestId
                }

                // Check if requestId exists in the dictionary
                if (!requestDictionary.hasOwnProperty(requestId)) {
                    return;
                }

                var requestDetails = requestDictionary[requestId];
                console.log(requestDetails); // Debugging purposes

                // Check if any of the details contain the search term
                var match = requestDetails.some(function (detail) {
                    return detail.toLowerCase().includes(searchTerm.toLowerCase());
                });

                // Show or hide the row based on the search match
                if (match) {
                    console.log("HIT - Showing row for requestId:", requestId);
                    $row.css("display", "table-row");  // Set display to table-row
                } else {
                    console.log("MISS - Hiding row for requestId:", requestId);
                    $row.css("display", "none");  // Hide row
                }
            });
        }

        // Handle search input
        $('.dataTables_filter input[type="search"]').on('input', function () {
            var searchTerm = $(this).val().trim();
            filterTable(searchTerm);
        });

        // Attach event listeners for toggling rows
        $('.toggle-btn').on('click', function () {
            var target = $(this).data('target');
            $(target).fadeToggle(500);
        });
    });
</script>
}
</body>



Thank you for your time and attention!