{"id":3481,"date":"2026-09-01T11:21:43","date_gmt":"2026-09-01T16:21:43","guid":{"rendered":"https:\/\/xlinesoft.com\/blog\/?p=3481"},"modified":"2026-09-01T11:21:43","modified_gmt":"2026-09-01T16:21:43","slug":"add-search-boxes-to-individual-dashboard-grids","status":"publish","type":"post","link":"https:\/\/xlinesoft.com\/blog\/2026\/09\/01\/add-search-boxes-to-individual-dashboard-grids\/","title":{"rendered":"Add Search Boxes to Individual Dashboard Grids"},"content":{"rendered":"<p>PHPRunner and ASPRunner.NET dashboards can display several grids on one page. In this tutorial, we will add a separate search box to each grid so users can filter Customers, Products, or Employees without affecting the other dashboard elements.<\/p>\n<p>The solution uses one reusable JavaScript function and a small amount of Custom CSS. The same code works in PHPRunner and ASPRunner.NET.<\/p>\n<p><a href=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search-1024x453.png\" alt=\"\" width=\"810\" height=\"358\" class=\"alignnone size-large wp-image-3482\" srcset=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search-1024x453.png 1024w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search-600x266.png 600w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search-768x340.png 768w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search-1536x680.png 1536w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/scr_dashboard_search.png 1843w\" sizes=\"auto, (max-width: 810px) 100vw, 810px\" \/><\/a><\/p>\n<p><!--more--><\/p>\n<h3>1. Preparing the dashboard<\/h3>\n<p>Create a dashboard and add the grid elements that need their own search boxes. This example uses the <strong>customers<\/strong>, <strong>products<\/strong>, and <strong>employees<\/strong> data sources.<\/p>\n<p>You will pass these data source names to the search function in section 3. Use the names as they appear in your project, not the visible dashboard panel captions.<\/p>\n<h3>2. Adding the dashboard grid search function<\/h3>\n<p>Open <strong>Events \u2192 custom_function.js<\/strong> and add the following JavaScript:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nfunction addDashboardGridSearch(dashboard, tableName) {\r\n    var dashboardElement = dashboard.elements.find(function(element) {\r\n        return element.table === tableName;\r\n    });\r\n\r\n    if (!dashboardElement) {\r\n        console.warn(\"Dashboard table not found: \" + tableName);\r\n        return;\r\n    }\r\n\r\n    var containerId =\r\n        \"dashelement_\" + dashboardElement.elementName + dashboard.id;\r\n    var container = document.getElementById(containerId);\r\n\r\n    if (!container) {\r\n        console.warn(\"Dashboard container not found: \" + containerId);\r\n        return;\r\n    }\r\n\r\n    if (container.getAttribute(\"data-grid-search-initialized\")) {\r\n        return;\r\n    }\r\n\r\n    container.setAttribute(\"data-grid-search-initialized\", \"1\");\r\n\r\n    var searchValue = \"\";\r\n\r\n    function search(input) {\r\n        searchValue = input.value.trim();\r\n\r\n        var parameters = {\r\n            goto: 1\r\n        };\r\n\r\n        if (searchValue) {\r\n            parameters.qs = searchValue;\r\n        } else {\r\n            parameters.a = \"return\";\r\n            parameters.qs = \"\";\r\n        }\r\n\r\n        dashboard.loadPage(\r\n            dashboardElement,\r\n            {\r\n                baseParams: parameters\r\n            }\r\n        );\r\n    }\r\n\r\n    function insertSearchBox() {\r\n        var heading = container.querySelector(\".panel-heading\");\r\n        var counter = container.querySelector(\r\n            \"[data-itemtype='details_found']\"\r\n        );\r\n\r\n        if (!heading || !counter) {\r\n            return;\r\n        }\r\n\r\n        if (heading.querySelector(\".dashboard-grid-search\")) {\r\n            return;\r\n        }\r\n\r\n        var searchBox = document.createElement(\"div\");\r\n        var input = document.createElement(\"input\");\r\n        var clearButton = document.createElement(\"button\");\r\n\r\n        searchBox.className = \"dashboard-grid-search\";\r\n\r\n        input.type = \"search\";\r\n        input.className = \"form-control input-sm\";\r\n        input.placeholder = \"Search\";\r\n        input.value = searchValue;\r\n        input.autocomplete = \"off\";\r\n        input.setAttribute(\"aria-label\", \"Search \" + tableName);\r\n\r\n        clearButton.type = \"button\";\r\n        clearButton.className = \"btn btn-default btn-sm\";\r\n        clearButton.innerHTML = \"&times;\";\r\n        clearButton.title = \"Clear search\";\r\n        clearButton.setAttribute(\r\n            \"aria-label\",\r\n            \"Clear \" + tableName + \" search\"\r\n        );\r\n\r\n        searchBox.appendChild(input);\r\n        searchBox.appendChild(clearButton);\r\n\r\n        heading.classList.add(\"dashboard-grid-search-heading\");\r\n        counter.parentNode.classList.add(\"dashboard-grid-search-count\");\r\n\r\n        heading.insertBefore(searchBox, counter.parentNode);\r\n\r\n        [\r\n            \"click\",\r\n            \"mousedown\",\r\n            \"mouseup\",\r\n            \"touchstart\"\r\n        ].forEach(function(eventName) {\r\n            searchBox.addEventListener(eventName, function(event) {\r\n                event.stopPropagation();\r\n            });\r\n        });\r\n\r\n        input.addEventListener(\"keydown\", function(event) {\r\n            if (event.key !== \"Enter\" && event.keyCode !== 13) {\r\n                return;\r\n            }\r\n\r\n            event.preventDefault();\r\n            event.stopPropagation();\r\n            search(input);\r\n        });\r\n\r\n        clearButton.addEventListener(\"click\", function(event) {\r\n            event.preventDefault();\r\n            event.stopPropagation();\r\n\r\n            input.value = \"\";\r\n            search(input);\r\n        });\r\n    }\r\n\r\n    insertSearchBox();\r\n\r\n    new MutationObserver(insertSearchBox).observe(\r\n        container,\r\n        {\r\n            childList: true,\r\n            subtree: true\r\n        }\r\n    );\r\n}<\/textarea><\/pre>\n<\/div>\n<p>The function finds one dashboard element by its data source name and adds a search control to that element&#8217;s panel heading. Pressing Enter passes the search phrase in <strong>qs<\/strong> and reloads only that grid. The clear button removes the stored search.<\/p>\n<p>Dashboard grids replace their markup after an AJAX refresh. The <strong>MutationObserver<\/strong> restores the search box after that replacement while preserving the current search phrase.<\/p>\n<h3>3. Enabling search for the required grids<\/h3>\n<p>Open <strong>Events \u2192 Dashboard \u2192 Dashboard page \u2192 JavaScript OnLoad<\/strong> and call the function once for each grid:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\naddDashboardGridSearch(pageObj, \"customers\");\r\naddDashboardGridSearch(pageObj, \"products\");\r\naddDashboardGridSearch(pageObj, \"employees\");<\/textarea><\/pre>\n<\/div>\n<p>Replace these values with the data source names used by your dashboard. Omit any dashboard element that should not have an individual search box.<\/p>\n<h3>4. Styling the search boxes<\/h3>\n<p>Open <strong>Style Editor<\/strong>, click &#8216;Modify CSS&#8217;, and add the following code:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\n.function-dashboard .panel-heading.dashboard-grid-search-heading {\r\n    display: flex;\r\n    align-items: center;\r\n    gap: 8px;\r\n    flex-wrap: nowrap;\r\n}\r\n\r\n.function-dashboard\r\n.panel-heading.dashboard-grid-search-heading\r\n> .rnr-dbebrick {\r\n    float: none !important;\r\n    white-space: nowrap;\r\n}\r\n\r\n.function-dashboard .dashboard-grid-search {\r\n    display: flex;\r\n    flex: 0 1 180px;\r\n    min-width: 120px;\r\n    margin-left: auto;\r\n}\r\n\r\n.function-dashboard .dashboard-grid-search .form-control {\r\n    height: 30px;\r\n    min-width: 0;\r\n    padding: 4px 8px;\r\n    font-size: 12px;\r\n    border-radius: 4px 0 0 4px;\r\n}\r\n\r\n.function-dashboard .dashboard-grid-search .btn {\r\n    height: 30px;\r\n    padding: 4px 9px;\r\n    font-size: 16px;\r\n    line-height: 16px;\r\n    border-radius: 0 4px 4px 0;\r\n}\r\n\r\n.function-dashboard .dashboard-grid-search-count {\r\n    flex: 0 0 auto;\r\n    margin-left: 0;\r\n    white-space: nowrap;\r\n    font-size: 12px;\r\n}\r\n\r\n@media (max-width: 900px) {\r\n    .function-dashboard\r\n    .panel-heading.dashboard-grid-search-heading {\r\n        flex-wrap: wrap;\r\n    }\r\n\r\n    .function-dashboard .dashboard-grid-search {\r\n        order: 3;\r\n        flex-basis: 100%;\r\n        margin-left: 28px;\r\n    }\r\n\r\n    .function-dashboard .dashboard-grid-search-count {\r\n        margin-left: auto;\r\n    }\r\n}<\/textarea><\/pre>\n<\/div>\n<p>The desktop layout places the search box beside the record counter. Below 900 pixels, it moves to a separate line so the panel heading remains usable on smaller screens.<\/p>\n<p>After rebuilding the project, open the Dashboard page and enter a phrase in one of the search boxes. Press Enter and confirm that only the corresponding grid is filtered. Click the clear button to restore the full grid, then repeat the test after collapsing and expanding the dashboard panel.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Add independent search boxes to dashboard grid elements in PHPRunner and ASPRunner.NET using JavaScript, AJAX grid reloads, and responsive CSS.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,1,8],"tags":[],"class_list":["post-3481","post","type-post","status-publish","format-standard","hentry","category-asp-net","category-php-category","category-tutorials"],"_links":{"self":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3481","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/comments?post=3481"}],"version-history":[{"count":1,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3481\/revisions"}],"predecessor-version":[{"id":3483,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3481\/revisions\/3483"}],"wp:attachment":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/media?parent=3481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/categories?post=3481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/tags?post=3481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}