Problem
Cisco Splunk Enterprise is awesome and the Splunk GUI is, too. However sometimes you need to quickly search the result data.
Solution
My coworker and friend Malte created a 100% client-side browser bookmarklet (see https://de.wikipedia.org/wiki/Bookmarklet) which enables you to search your data in the browser GUI fast and easily. Works fine with Firefox and Chrome with Splunk Enterprise v10.0 and v10.2.
Demo:
How it works under the hood
There is no magic - just client-side browser DOM work:- It finds Splunks classic result tables by their CSS classes (table-chrome, results-table, and friends).
- It injects a scoped stylesheet (all classes are namespaced with bm-, for bookmarklet) so it never clobbers Splunk's own styles.
- Filtering is pure client-side: it reads each cell's text, compares it against your tags, and toggles row visibility.
- Page merging watches the paginator, waits for the active page to change, then splices the new rows back into the master table.
Everything is client-side: no external network calls, no telemetry, no data leaves the page.
Caveats
It is built for Cisco Splunks classic UI. So it won't work on the newer Splunk Dashboard Studio dashboards. And the page-merge feature does re-trigger searches as it clicks through — handy, but worth using gently on a busy shared search head.Installation
- Open Browser (Chrome, Firefox)
- Create a bookmark and insert the following javascript as URL:

https://github.com/flostyen/SplunkMalteBeautifier/blob/main/bookmarkletjavascript:(function(){const splunkTables=document.querySelectorAll('.results-table .table, table.table-chrome');if(splunkTables.length===0){alert('No classic Splunk dashboard or search tables found.');return}if(!document.getElementById('splunk-bookmarklet-styles')){const style=document.createElement('style');style.id='splunk-bookmarklet-styles';style.innerHTML='.bm-top-bar { display: flex; align-items: center; margin-bottom: 5px; flex-wrap: wrap; gap: 8px; } .bm-stats-label { margin-left: 5px; font-size: 12px; color: #555; } .bm-filter-row th { border-bottom: 1px solid #ccc !important; background: #f5f5f5; padding: 4px !important; vertical-align: top; font-weight: normal; } .bm-filter-input { width: 100%; border: 1px solid #ccc; border-radius: 3px; padding: 2px 4px; font-size: 11px; box-sizing: border-box; } .bm-filter-input:focus { outline: none; border-color: #5cc05c; } .bm-settings-btn { cursor: pointer; background: #eee; border: 1px solid #ccc; padding: 0 8px; border-radius: 4px; font-size: 12px; height: 26px; display: inline-flex; align-items: center; box-sizing: border-box; } .bm-settings-btn:hover { background: #ddd; } .bm-merge-group { display: flex; align-items: stretch; border: 1px solid #ccc; border-radius: 4px; overflow: hidden; height: 26px; box-sizing: border-box; } .bm-merge-label { display: flex; align-items: center; padding: 0 6px; font-size: 12px; background: #f5f5f5; color: #555; border-right: 1px solid #ccc; } .bm-merge-input { width: 40px; border: none !important; padding: 0; font-size: 13px; font-weight: bold; text-align: center; outline: none; box-sizing: border-box; box-shadow: none !important; } .bm-merge-input::-webkit-outer-spin-button, .bm-merge-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .bm-merge-input[type=number] { -moz-appearance: textfield; } .bm-merge-btn { cursor: pointer; background: #dbeafe; color: #1e40af; border: none; border-left: 1px solid #a5b4fc; padding: 0 8px; font-size: 12px; font-weight: bold; } .bm-merge-btn:hover { background: #bfdbfe; } .bm-merge-btn:disabled, .bm-merge-input:disabled { opacity: 0.6; cursor: not-allowed; background: #f3f4f6; } .bm-tag-container { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 4px; } .bm-tag { display: inline-flex; align-items: center; background: #e0e7ff; border: 1px solid #a5b4fc; border-radius: 3px; padding: 1px 4px; font-size: 10px; color: #3730a3; word-break: break-all; } .bm-tag.bm-tag-negative { background: #fee2e2; border-color: #fca5a5; color: #991b1b; } .bm-tag-remove { cursor: pointer; font-weight: bold; margin-left: 4px; opacity: 0.6; } .bm-tag-remove:hover { opacity: 1; } .bm-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 999999; display: flex; align-items: center; justify-content: center; } .bm-modal { background: #fff; padding: 15px; border-radius: 6px; min-width: 300px; max-height: 80vh; overflow-y: auto; font-size: 13px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); } .bm-modal h3 { margin-top: 0; margin-bottom: 10px; font-size: 16px; border-bottom: 1px solid #eee; padding-bottom: 5px; } .bm-modal label { display: block; margin-bottom: 5px; cursor: pointer; } .bm-modal-close { margin-top: 15px; background: #0073e7; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; width: 100%; } .bm-modal-close:hover { background: #005bb5; } mark.bm-highlight { background-color: #ffeb3b !important; color: #000 !important; border-radius: 2px; padding: 0 2px; }';document.head.appendChild(style)}function toggleColumn(table,colIndex,isVisible){const displayStyle=isVisible?'':'none';const theadTrs=table.querySelectorAll('thead tr');theadTrs.forEach(tr=>{const th=tr.querySelector(`th:nth-child(${colIndex+1})`);if(th)th.style.display=displayStyle});const tbodyTrs=table.querySelectorAll('tbody tr');tbodyTrs.forEach(tr=>{const td=tr.querySelector(`td:nth-child(${colIndex+1})`);if(td)td.style.display=displayStyle})}function createTag(container,term,table,filterRow){const isNegative=term.startsWith('!');const tag=document.createElement('span');tag.className='bm-tag'+(isNegative?' bm-tag-negative':'');tag.dataset.term=term;const text=document.createElement('span');text.textContent=term;tag.appendChild(text);const removeBtn=document.createElement('span');removeBtn.className='bm-tag-remove';removeBtn.innerHTML='✕';removeBtn.onclick=()=>{tag.remove();applyFilters(table,filterRow)};tag.appendChild(removeBtn);container.appendChild(tag)}function createInputNodes(table,filterRow){const input=document.createElement('input');input.type='text';input.className='bm-filter-input';input.placeholder='Enter tags...';input.title='Press Enter to create tags (! for exclude)';const tagContainer=document.createElement('div');tagContainer.className='bm-tag-container';input.onkeydown=(e)=>{if(e.key==='Enter'){e.preventDefault();const val=input.value.trim();if(val){const terms=val.split(/\s+/).filter(t=>t.length>0);terms.forEach(term=>createTag(tagContainer,term,table,filterRow));input.value='';applyFilters(table,filterRow)}}};input.onkeyup=(e)=>{if(e.key!=='Enter'){applyFilters(table,filterRow)}};return{input,tagContainer}}function removeHighlights(root){const marks=Array.from(root.querySelectorAll('mark.bm-highlight'));marks.forEach(mark=>{const parent=mark.parentNode;if(parent){parent.replaceChild(document.createTextNode(mark.textContent),mark);parent.normalize()}})}function highlightNode(node,terms){if(node.nodeType===3){const text=node.nodeValue;const lowerText=text.toLowerCase();let matchFound=false;let matchStart=-1;let matchLength=0;for(let i=0;i<terms.length;i++){const term=terms[i];if(!term)continue;const idx=lowerText.indexOf(term);if(idx!==-1){if(!matchFound||idx<matchStart){matchStart=idx;matchLength=term.length;matchFound=true}}}if(matchFound){const matchText=text.substring(matchStart,matchStart+matchLength);const beforeText=text.substring(0,matchStart);const afterText=text.substring(matchStart+matchLength);const mark=document.createElement('mark');mark.className='bm-highlight';mark.textContent=matchText;const parent=node.parentNode;if(!parent||parent.tagName==='SCRIPT'||parent.tagName==='STYLE'||parent.tagName==='MARK')return;if(beforeText){parent.insertBefore(document.createTextNode(beforeText),node)}parent.insertBefore(mark,node);const afterNode=document.createTextNode(afterText);parent.insertBefore(afterNode,node);parent.removeChild(node);highlightNode(afterNode,terms)}}else if(node.nodeType===1&&node.childNodes&&!/(script|style|mark)/i.test(node.tagName)){Array.from(node.childNodes).forEach(child=>highlightNode(child,terms))}}function enhanceTable(table,tableIndex){table.dataset.bmIndex=tableIndex;table.bmAccumulatedRows=table.bmAccumulatedRows||[];const isEventTable=table.classList.contains('events-results-table')||table.classList.contains('events-table');const thead=table.querySelector('thead');if(!thead)return;const headerRow=thead.querySelector('tr');if(!headerRow)return;if(headerRow.nextElementSibling&&headerRow.nextElementSibling.classList.contains('bm-filter-row'))return;const thElements=Array.from(headerRow.querySelectorAll('th'));const colNames=thElements.map(th=>{const firstA=th.querySelector('a');return firstA?firstA.textContent.trim():th.textContent.trim()});const topBar=document.createElement('div');topBar.className='bm-top-bar';const settingsBtn=document.createElement('button');settingsBtn.innerHTML='\u2699\uFE0F Column Settings';settingsBtn.className='bm-settings-btn';settingsBtn.onclick=(e)=>{e.preventDefault();openSettings(table,colNames)};const mergeGroup=document.createElement('div');mergeGroup.className='bm-merge-group';const mergeLabel=document.createElement('span');mergeLabel.className='bm-merge-label';mergeLabel.innerHTML='\u2795 Next';const mergeInput=document.createElement('input');mergeInput.type='number';mergeInput.min='1';mergeInput.value='1';mergeInput.className='bm-merge-input';mergeInput.title='Number of pages to merge';const mergeBtn=document.createElement('button');mergeBtn.innerHTML='Pages';mergeBtn.className='bm-merge-btn';mergeGroup.appendChild(mergeLabel);mergeGroup.appendChild(mergeInput);mergeGroup.appendChild(mergeBtn);const statsLabel=document.createElement('span');statsLabel.id=`bm-stats-${tableIndex}`;statsLabel.className='bm-stats-label';const filterRow=document.createElement('tr');filterRow.className='bm-filter-row';mergeBtn.onclick=(e)=>{e.preventDefault();const pagesToMerge=parseInt(mergeInput.value,10)||1;if(pagesToMerge<1)return;mergeBtn.disabled=true;mergeInput.disabled=true;function doMerge(remaining){if(remaining<=0){mergeBtn.innerHTML='Pages';mergeBtn.disabled=false;mergeInput.disabled=false;return}mergeBtn.innerHTML=`\u23F3 (${remaining})...`;let paginator=null;let pNode=table.parentElement;while(pNode&&pNode!==document.body){paginator=pNode.querySelector('.splunk-paginator, .shared-searchresultspaginator');if(paginator)break;pNode=pNode.parentElement}if(!paginator){alert('Paginator not found for this table.');mergeBtn.innerHTML='Pages';mergeBtn.disabled=false;mergeInput.disabled=false;return}const nextBtn=paginator.querySelector('a[data-page="next"], li.next a');let isDisabled=!nextBtn;if(nextBtn){if(nextBtn.classList.contains('disabled')||nextBtn.getAttribute('aria-disabled')==='true'||(nextBtn.parentElement&&nextBtn.parentElement.classList.contains('disabled'))){isDisabled=true}}if(isDisabled){mergeBtn.innerHTML='Pages';mergeBtn.disabled=false;mergeInput.disabled=false;return}const currentActive=paginator.querySelector('a.selected, li.active a');const currentActivePage=currentActive?currentActive.textContent.trim():null;const tbody=table.querySelector('tbody');if(tbody){const nativeRows=Array.from(tbody.querySelectorAll('tr:not(.bm-filter-row):not(.bm-merged-row)'));nativeRows.forEach(r=>{const clone=r.cloneNode(true);clone.classList.add('bm-merged-row');table.bmAccumulatedRows.push(clone)})}nextBtn.click();let attempts=0;const checkInterval=setInterval(()=>{attempts++;const newActive=paginator.querySelector('a.selected, li.active a');const newActivePage=newActive?newActive.textContent.trim():null;if(newActivePage!==currentActivePage){clearInterval(checkInterval);setTimeout(()=>{const newTbody=table.querySelector('tbody');if(newTbody){table.bmAccumulatedRows.slice().reverse().forEach(tr=>{newTbody.insertBefore(tr,newTbody.firstChild)})}if(!table.querySelector('.bm-filter-row')){const currentThead=table.querySelector('thead');if(currentThead)currentThead.appendChild(filterRow)}if(!isEventTable){const ths=table.querySelectorAll('thead tr:first-child th');ths.forEach((th,idx)=>{if(th.style.display==='none')toggleColumn(table,idx,false)})}applyFilters(table,filterRow);doMerge(remaining-1)},300)}else if(attempts>50){clearInterval(checkInterval);alert('Timed out waiting for Splunk to load the next page.');mergeBtn.innerHTML='Pages';mergeBtn.disabled=false;mergeInput.disabled=false}},200)}doMerge(pagesToMerge)};if(!isEventTable){topBar.appendChild(settingsBtn)}topBar.appendChild(mergeGroup);topBar.appendChild(statsLabel);if(table.parentElement.classList.contains('shared-resultstable-resultstablemaster')||table.parentElement.classList.contains('table-wrapper')){table.parentElement.parentNode.insertBefore(topBar,table.parentElement)}else{table.parentNode.insertBefore(topBar,table)}thElements.forEach((th)=>{const filterCell=document.createElement('th');filterCell.style.cssText=th.style.cssText;filterCell.className=th.className;filterCell.classList.remove('sorts');filterCell.style.padding='4px';const colText=th.textContent.trim();if(isEventTable){if(colText==='Event'||colText==='Time'){const nodes=createInputNodes(table,filterRow);filterCell.appendChild(nodes.input);filterCell.appendChild(nodes.tagContainer)}}else{const nodes=createInputNodes(table,filterRow);filterCell.appendChild(nodes.input);filterCell.appendChild(nodes.tagContainer)}filterRow.appendChild(filterCell)});thead.appendChild(filterRow);applyFilters(table,filterRow)}function applyFilters(table,filterRow){const isEventTable=table.classList.contains('events-results-table')||table.classList.contains('events-table');const tbody=table.querySelector('tbody');if(!tbody)return;const trs=tbody.querySelectorAll('tr:not(.bm-filter-row)');const filterCells=filterRow.querySelectorAll('th');let visibleCount=0;const totalCount=trs.length;trs.forEach(tr=>{let rowVisible=true;removeHighlights(tr);const cellHighlightMap=new Map();const tds=tr.querySelectorAll('td');filterCells.forEach((cell,index)=>{if(!rowVisible)return;const input=cell.querySelector('input');if(!input)return;const tagContainer=cell.querySelector('.bm-tag-container');let terms=[];const tags=tagContainer.querySelectorAll('.bm-tag');tags.forEach(tag=>terms.push(tag.dataset.term));const inputText=input.value.trim().toLowerCase();if(inputText){terms=terms.concat(inputText.split(/\s+/).filter(t=>t.length>0))}if(terms.length>0){let cellText='';if(isEventTable){cellText=tr.textContent.toLowerCase()}else{const td=tds[index];if(td)cellText=(td.textContent||td.innerText).toLowerCase()}if(cellText){for(let i=0;i<terms.length;i++){const term=terms[i].toLowerCase();const isNegative=term.startsWith('!');const actualTerm=isNegative?term.substring(1):term;if(actualTerm==='')continue;const hasTerm=cellText.includes(actualTerm);if(isNegative&&hasTerm){rowVisible=false;break}if(!isNegative&&!hasTerm){rowVisible=false;break}}if(rowVisible){const posTerms=terms.filter(t=>!t.startsWith('!')).map(t=>t.toLowerCase());if(posTerms.length>0){if(isEventTable){if(!cellHighlightMap.has(tr))cellHighlightMap.set(tr,new Set());posTerms.forEach(t=>cellHighlightMap.get(tr).add(t))}else{const td=tds[index];if(td){if(!cellHighlightMap.has(td))cellHighlightMap.set(td,new Set());posTerms.forEach(t=>cellHighlightMap.get(td).add(t))}}}}}else{rowVisible=false}}});if(rowVisible){visibleCount++;cellHighlightMap.forEach((termSet,element)=>{highlightNode(element,Array.from(termSet))})}tr.style.display=rowVisible?'':'none'});const statsLabel=document.getElementById(`bm-stats-${table.dataset.bmIndex}`);if(statsLabel){const hiddenCount=totalCount-visibleCount;statsLabel.innerHTML=`Showing <strong>${visibleCount}</strong> of <strong>${totalCount}</strong> rows (<strong>${hiddenCount}</strong> filtered)`}}function openSettings(table,colNames){const overlay=document.createElement('div');overlay.className='bm-modal-overlay';const modal=document.createElement('div');modal.className='bm-modal';modal.innerHTML='<h3>\u2699\uFE0F Toggle Columns</h3>';colNames.forEach((name,idx)=>{const label=document.createElement('label');const cb=document.createElement('input');cb.type='checkbox';const th=table.querySelector(`thead tr th:nth-child(${idx+1})`);cb.checked=th&&th.style.display!=='none';cb.onchange=(e)=>{toggleColumn(table,idx,e.target.checked)};label.appendChild(cb);label.appendChild(document.createTextNode(' '+(name||`Column ${idx+1}`)));modal.appendChild(label)});const closeBtn=document.createElement('button');closeBtn.className='bm-modal-close';closeBtn.innerHTML='Close';closeBtn.onclick=()=>overlay.remove();modal.appendChild(closeBtn);overlay.appendChild(modal);document.body.appendChild(overlay)}splunkTables.forEach((table,index)=>enhanceTable(table,index))})();
Or see here: https://raw.githubusercontent.com/flostyen/SplunkMalteBeautifier/refs/heads/main/bookmarklet - Open Splunk GUI and execute a search
- Click on the bookmark
- The bookmark javascript code is executed on the Splunk GUI local cache of your browser

No comments:
Post a Comment