So I'm working on a feature that require a user to type in a search item that will search a table for the term and highlight every occurrence. I found some code and got it to work, but I have a bug that I'm not sure how to resolve. In the search input if I hit back space to erase the search term, the unhighlight works until the last character letter. For example, I type in Demo and use back space to delete Demo, only the D is still highlighted.
Below are my code snippets.
NOTE: I have a search and render function that call the highlightActivity and renderReports functions
My CSS
.highlight {
background-color :gold
}
.highlight {
padding:1px 4px;
margin: 0 -4px;
}
My HTML
<input type="text" id="uniStringSearch" onkeyup="SearchAndRender()"></input>
<table id="currentReports" class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th><input name="select_all" id="selectedReports" value="1" type="checkbox" onclick="checkAll(this)" /> </th>
<th>DATE</th>
<th>SUMMARY TITLE</th>
<th>NAME</th>
<th>ACTIVITY TYPE</th>
<th>SUB ACTIVITY TYPE</th>
<th>ACTIVITY DETAILS</th>
</tr>
</thead>
<tbody id="reports"></tbody>
</table>
HighlightActivity Function
function highlightActivity() {
var searchTerm = $('#uniStringSearch').val();
var tableID = ''
switch (webPage) {
case 'reports':
tableID = '#currentReports';
break;
case 'analysis':
tableID = '#currentAnalysis';
break;
case 'statuses':
tableID = '#currentStatuses';
break;
default:
}
$(tableID).find('.highlight').removeClass('highlight');
$(tableID).find('tr td').each(function () {
if ((this).attr('data-search') !== 'false') {
var text = $(this).text();
var textLowerCase = text.toLowerCase();
var pos = textLowerCase.indexOf(searchTerm.toLowerCase());
var regex = new RegExp(searchTerm, 'ig');
text = text.replace(regex, (match, $1) => {
return '<span class="highlight">' + match + '</span>';
});
$(this).html(text);
if (pos !== -1) {
setTimeout(function () {
if ($(this).parent().find('.highlight').is(':empty')) {
$('.highlight').remove();
}
}.bind(this), 0);
}
else {
$(this).text(text);
}
}
if ($(this).parent().find('.highlight').length > 0) {
$(this).parent().show();
}
else {
$(this).parent().hide();
}
});
}
Render Reports Function
function renderReports(items) {
var html = '';
for (var i = 0; i < items.length; i++)
{
html = '<tr><td><input type="checkbox" id="selectedReports" onchange="chkInputChanged(event)"/></td>'
html += '<td>' + items[i].DateActivity + '</td>'
html += '<td>' + items[i].Name + '</td>'
html += '<td>' + items[i].ActivityType + '</td>'
html += '<td>' + items[i].SubActivityType + '</td>'
html += '<td id="searchActivityDetails">' + items[i].ActivityDetails + '</td></tr>'
}
$('#reports').empty();
$('#reports').append(html);
}
ALSO....one more thing, is there a way to have my highlight function to target a specific column? For example Summary Title and Activity Details
Related
I have this table in which I read the tbody contents from a JSON API and now I need to make it sortable by columns and using only javascript and no Jquery.
Any help would be appreciated
i have found this code which is exactly what i want, but i don't know how to refer to tbodies from my api
var myRequest = new XMLHttpRequest();
myRequest.open('GET', 'https://jsonplaceholder.typicode.com/todos');
myRequest.onload = function () {
var myData = JSON.parse(myRequest.responseText);
dataTable(myData);
};
myRequest.send();
function dataTable(data) {
if (data.length > 0) {
var temp = '';
data.forEach((u) => {
temp += '<tr>';
temp += "<td style='text-align: center'>" + u.userId + '</td>';
temp += "<td style='text-align: center'>" + u.id + '</td>';
temp += '<td>' + u.title + '</td>';
temp += "<td style='text-align: center'>" + u.completed + '</td></tr>';
document.getElementById('data').innerHTML = temp;
});
}
}
<table class="table_id">
<thead>
<tr>
<th>UserID</th>
<th>ID</th>
<th>Title</th>
<th>Completion</th>
</tr>
</thead>
<tbody id="data">
</tbody>
</table>
var myData, asc = {'userId':true, 'id':true, 'title':true, 'completed':true};
var myRequest = new XMLHttpRequest();
myRequest.open('GET', 'https://jsonplaceholder.typicode.com/todos');
myRequest.onload = function () {
myData = JSON.parse(myRequest.responseText);
dataTable(myData);
};
myRequest.send();
function sortTable(key){
myData.sort(function(a, b) {
if(asc[key]){
return a[key] > b[key]? 1:-1;
}
else{
return a[key] > b[key]? -1:1;;
}
});
asc[key] = !asc[key];
document.getElementById('data').innerHTML = '';
dataTable(myData);
}
function dataTable(data) {
if (data.length > 0) {
var temp = '';
data.forEach((u) => {
temp += '<tr>';
temp += "<td style='text-align: center'>" + u.userId + '</td>';
temp += "<td style='text-align: center'>" + u.id + '</td>';
temp += '<td>' + u.title + '</td>';
temp += "<td style='text-align: center'>" + u.completed + '</td></tr>';
document.getElementById('data').innerHTML = temp;
});
}
}
<table class="table_id">
<thead>
<tr>
<th onclick="sortTable('userId');">UserID</th>
<th onclick="sortTable('id');">ID</th>
<th onclick="sortTable('title');">Title</th>
<th onclick="sortTable('completed');">Completion</th>
</tr>
</thead>
<tbody id="data">
</tbody>
</table>
Here is a quick class I made.
You first load your data with dt.load(), then when someone clicks one of the headers, you can add an event that does:
elm.addEventListener("click", (e) => {
prop = e.target.textContent.trim();
dt.sort(prop);
dt.render();
})
class DataTable{
load(arr){
if(arr) this.data = arr;
}
sort(prop){
this.data.sort((a, b) => a[prop] - b[prop]);
}
render(selector="#data"){
if(data.length){
html = data.map(u => {
return [
"<tr>",
`<td style='text-align: center'>${u.userId}</td>`,
`<td style='text-align: center'>${u.id}</td>`,
`<td>${u.title}</td>`,
`<td style='text-align: center'>${u.completed}</td></tr>`
].join("");
}).join("");
document.querySelector(selector).innerHTML = html;
}
}
}
Important - are you wanting to be able to sort multiple columns at the same time or just one column at a time?
Initialize myData outside of onload first. You'll want to be able to access those results outside of onload to sort them. The actual sort function JS offers is a pretty confusing but it's really the only way to go about vanilla JS array sorting.
function sortData(col, asc = true) {
//JS sort function
myData = myData.sort((first, second) => {
//sort logic
if (first[col] == second[col]) {
return 0;
}
if (asc) {
if (first[col] > second[col]) {
return 1;
}
else {
return -1;
}
}
else {
if (first[col] > second[col]) {
return -1;
}
else {
return 1;
}
}
});
//Re-Create table
dataTable(myData);
}
EDIT:
I added in sort logic, but it's definitely possible I messed up. I can't actually test this right now and I haven't touched the sort function in years.
I am running a code that scrapes a form and then opens a new browser tab with the data using the format: fieldID, fieldLabel, filedValue
jQuery(document).ready(function(){jQuery('#helpdesk_ticket_submit').on('click', function(e){
submitText = "<table class='table'>"
submitText += ` <thead>
<tr>
<th scope="col">fieldID</th>
<th scope="col">fieldLabel</th>
<th scope="col">fieldValue</th>
</tr>
</thead>
<tbody>`
jQuery('#new_helpdesk_ticket input, #new_helpdesk_ticket select, #new_helpdesk_ticket textarea').each(
function(index){
var input = jQuery(this);
if (input.prev().is('textarea'))
if (input.attr('class').indexOf('hide') >= 0)
return;
let label = extractLabelText(input.attr('id')) //extrenal function to get the label of the field
submitText += "<tr><td>" + input.attr('id') + "</td><td>" + label + "</td><td> " + input.val() + "</td></tr>"
}
);
window.open().document.write(submitText);
there is nothing wrong with the code. My question is when I do window.open().document.write(submitText); how can I format the table with bootstrap?
I create an dyamic form with Jquery, there will be the multiple select box and textbox, how can I group the data into one based on the user name. For example, there will be the multiple select box = lim, total = 20, how can I group this 2 into array as 1.
When click the save button the final data will be like below
array(
'lim' => 40,
'tan' => 10,
);
Code here: https://jsfiddle.net/7gbvfjdc/
You mean something like this ?
You save button event listener should have following code
$('.savebtn').on('click', function() {
var mapObj = {};
$('.listable .cb').each(function(index, item) {
var selectVal = $(this).find('select').val();
if (mapObj[selectVal]) {
mapObj[selectVal] += Number($(this).find('#amt1_' + index).val());
} else {
mapObj[selectVal] = Number($(this).find('#amt1_' + index).val());
}
});
console.log(mapObj);
});
var i = 0;
$('.addRow').on('click', function() {
addRow();
});
function addRow() {
var tr = '<tr class="cb" id="row_' + i + '"><td>';
tr += '<select class="form-control select2" id="name1_' + i + ' first" name="name[]">';
tr += '<option>tan</option><option>lim</option></select></td>';
tr += '<td><input type="number" name="winlose[]" id="amt1_' + i + '" class="form-control"></td>';
tr += '<td style="text-align:center">-';
tr += '</td></tr>';
i++;
$('tbody').append(tr);
}
$('tbody').on('click', '.remove', function() {
$(this).parent().parent().remove();
});
$('.savebtn').on('click', function() {
var mapObj = {};
$('.listable .cb').each(function(index, item) {
var selectVal = $(this).find('select').val();
if (mapObj[selectVal]) {
mapObj[selectVal] += Number($(this).find('#amt1_' + index).val());
} else {
mapObj[selectVal] = Number($(this).find('#amt1_' + index).val());
}
});
console.log(mapObj);
});
<table class="table table-bordered listable">
<thead>
<tr class="text-center">
<th>name</th>
<th>amount</th>
<th style="text-align:center">+</th>
</tr>
</thead>
<tbody class="text-center">
</tbody>
</table>
<button type="button" class="btn btn-primary savebtn">Save</button>
You can use reduce on the body trs to extract the data and sum it in the wanted object format. Like this:
const result = $('tbody tr').get().reduce((prev, ne) => {
const $this = $(ne);
const type = $this.find('select').val();
prev[type] += parseInt($this.find('input').val())
return prev;
}, {
lim: 0,
tan: 0
});
var i = 0;
$('.addRow').on('click', function() {
addRow();
/*
$('.select2').select2({
theme: 'bootstrap4',
ajax: {
url: '{{ route("getMember") }}',
dataType: 'json',
},
}); */
});
function addRow() {
i++;
var tr = '<tr id="row_' + i + '"><td>';
tr += '<select class="form-control select2" id="name1_' + i + ' first" name="name[]">';
tr += '<option>tan</option><option>lim</option></select></td>';
tr += '<td><input type="number" name="winlose[]" id="amt1_' + i + '" class="form-control"></td>';
/* tr += '<td><select class="form-control select2" id="name2_'+i+'" name="name2[]">';
tr += '<option>tan</option><option>lim</option></select></td>';
tr += '<td><input type="number" name="winlose[]" id="amt2_'+i+'" class="form-control"></td>'; */
tr += '<td style="text-align:center">-';
tr += '</td></tr>';
$('tbody').append(tr);
}
$('tbody').on('click', '.remove', function() {
$(this).parent().parent().remove();
});
$('button').on('click', () => {
const result = $('tbody tr').get().reduce((prev, ne) => {
const $this = $(ne);
const type = $this.find('select').val();
prev[type] += parseInt($this.find('input').val())
return prev;
}, {
lim: 0,
tan: 0
});
console.log(result)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-bordered">
<thead>
<tr class="text-center">
<th>name</th>
<th>amount</th>
<th>Second name</th>
<th>Second amount</th>
<th style="text-align:center">+</th>
</tr>
</thead>
<tbody class="text-center">
</tbody>
</table>
<button>
save
</button>
https://jsfiddle.net/moshfeu/20kczto7/14/
I am trying to build a search function that would allow me to search Word 1 Word 2 ... Word 'n'.
The code below allows me to search through table rows. Results are presented when there is a 1:1 match (Ignoring case). I would like to search using combinations separated by spaces.
Table data sample as below.
AAA_BBB_CCC_DDD.pdf
EEE_FFF_GGG_HHH.pdf
HTML
<script>
$(function(){
$(function(){
var requestUri = "<<URL>>/_api/web/GetFolderByServerRelativeUrl('<<Folder>>')/Files?$filter=(substringof(%27.pdf%27,Name)%20or%20substringof(%27.PDF%27,Name))&$top=1000";
$.ajax({
url: requestUri,
type: "GET",
headers: {
"accept":"application/json; odata=verbose"
},
success: onSuccess,
});
function onSuccess(data) {
var objItems = data.d.results;
var tableContent = '<table id="Table" style="width:100%"><tbody>';
for (var i = 0; i < objItems.length; i++) {
tableContent += '<tr>';
tableContent += '<td>' + [i+1] + '</td>';
tableContent += '<td>' + objItems[i].Name + '</td>';
tableContent += '<td>' + "<a target='iframe_j' href='<<URL>>" + objItems[i].ServerRelativeUrl + "'>" + "View" + "</a>" + '</td>';
tableContent += '</tr>';
}
$('#TDGrid').append(tableContent);
}
});
});
</script>
<div id="div">
<input class="form-control mb-2" id="TDSearch" type="text" placeholder=" Search">
<table id='Table' class="table table-striped table-sm small">
<tr>
<td>
<div id="TDGrid" style="width: 100%"></div>
</td>
</tr>
</table>
</div>
Current search function
$(document).ready(function(){
$("#TDSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#TDGrid tr").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
You can convert string to array and then make a function, which will check match for strings of that array.
Something like
$(document).ready(function() {
$("#TDSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
var valueArr = value.split(' ');
$("#TDGrid tr").filter(function() {
$(this).toggle(checkIfValuePresent($(this).text().toLowerCase(), valueArr));
});
});
});
function checkIfValuePresent(currRowText, valuesarr) {
let isfound = false;
for (let i = 0; i < valuesarr.length; i++) {
if (currRowText.indexOf(valuesArr[i] > -1)) {
isfound = true;
break;
}
}
return isfound;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='text' id='TDSearch'>
split on space, join all strings with | between them for a combined regex string search, case insensitive.
$(document).ready(function(){
$("#TDSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#TDGrid tr").filter(function() {
$(this).toggle(new RegExp(value.replace(/\./g,'[.]').split(' ').join('|'),'gi').test($(this).text()))
});
});
});
I'm trying to make an editable table such that when the user clicks on the 'edit' button, every table data cell get placed inside an input form that the user can type in and change the information. Once the user is done, they may click the edit button again so that all of the input fields go away and the changes made are saved and displayed on the table.
I have made it so that every single data in every table data cell gets placed inside an input field when the user clicks the single 'edit' button. However, I'm having a really rough time trying to figure out how to remove the input boxes and display all the updated table cells. I was thinking of placing "contenteditable" withing every td and changing it to true/false would work, but I couldn't figure it out.
I'm using local storage for this, but I just need help on this one thing. Any help would be greatly appreciated.
var retrieveContacts = localStorage.getItem("contacts");
var newVariable = JSON.parse(retrieveContacts);
var isEdit = 0; // is the table in edit mode? 0- false 1- true
// set to 0 (false) by default
$.each(newVariable, function(){
$('#tableStyles').append('<tr>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].email + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].firstname + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].lastname + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].prefix + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].title + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].company + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].phone + '</td>' +
'<td id="tableCells" contenteditable="false">' + newVariable[i].fax + '</td>' +
'</tr>');
i++;
});
$('#createCont').click(function(){
var newRow = "<tr style='height: 35px;'><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>";
$('#tableStyles').append(newRow);
newVariable.push({"email": "",
"firstname": "",
"lastname": "",
"prefix": "",
"title": "",
"company": "",
"phone": "",
"fax": ""});
localStorage.setItem("contacts", JSON.stringify(newVariable));
});
$('#editCont').click(function(){
if(isEdit == 0){
var j = 0;
var trCount = 2; // up to newVariable.length+1
var tdCount = 1; // up to 8
for(trCount; trCount < newVariable.length+2; trCount++){
for(tdCount; tdCount < 9; tdCount++){
var testing1 = $("tr:nth-child(" + trCount + ")").children("td:nth-child(" + tdCount + ")");
var testing2 = testing1.html("<input type='text' value='" + testing1.html() + "'/>");
}
tdCount = 1;
}
trCount = 2;
tdCount = 1;
isEdit = 1;
//console.log("isEdit set to 1");
} else if(isEdit == 1) { // if the edit button is clicked and we are already editing the form,
// then we have take out the input boxes and save all changes.
for(trCount; trCount < newVariable.length+2; trCount++){
for(tdCount; tdCount < 9; tdCount++){
var testing1 = $("tr:nth-child(" + trCount + ")").children("td:nth-child(" + tdCount + ")");
}
tdCount = 1;
}
isEdit = 0;
//console.log("isEdit set to " + isEdit);
}
});
I would like to offer you a better solution. You can place the input field directly into the table cells and use the readonly attribute to set it editable.
Here is the code:
document.getElementById("edit").addEventListener("click", function() {
var fields = document.querySelectorAll("table input[type='text']");
for (var i = 0; i < fields.length; i++) {
fields[i].readOnly = false;
}
document.getElementById("save").style.display = "inline-block";
});
document.getElementById("save").addEventListener("click", function() {
var data = {};
data.name = document.getElementById("name").value;
data.email = document.getElementById("email").value;
// window.localStorage.formData = JSON.stringify(data);
// localStorage will not work in this snippet editor
// uncomment it in your code
var fields = document.querySelectorAll("table input[type='text']");
for (var i = 0; i < fields.length; i++) {
fields[i].readOnly = true;
}
document.getElementById("save").style.display = "none";
});
table input[type="text"] {
/* place any styling here */
}
table input[type="text"]:read-only {
border: none;
}
#save {
display: none;
}
<form>
<table>
<tr>
<td>Name:</td>
<td><input type="text" id="name" value="Some Name" readonly /></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="text" id="email" value="Email address" readonly /></td>
</tr>
</table>
<input type="button" id="edit" value="Edit" />
<input type="button" id="save" value="Save" />
</form>
Please, tell me if it works for you!