I have a table that contains v-html results (thus the text inside the table would not be there until the page is rendered). I would like to compare two rows and if they have duplicate words then they should be highlighted.
Here is an example project of what I wanted, but way beyond the scope of what I need. My question seems most like this one in the stacks but it requires that the words be defined, I want the page to find them itself.
For example, this would be the expected output:
<table>
<tr>
<td v-html="link.orderdesciption">
order:<br />
<mark> TV </mark><br /> <!--note that the contents of the td would not appear in markup due to being v-html-->
PS3 <br />
Laptop
</td>
<td>
qty:<br />
1<br />
2<br />
1<br />
</td>
</tr>
<tr>
<td>
----------------
</td>
<td>
----------------
</td>
</tr>
<tr>
<td v-html="link.orderrecieved">
recieved:<br /> <!--same note as above, v-html only shows-->
<mark> TV </mark><br />
Desktop<br />
</td>
</tr>
</table>
I've been working on this but I have really no idea where to go from here:
var text = $('td').text(),
words = text.split(' '),
sortedWords = words.slice(0).sort(),
duplicateWords = [];
for (var i=0; i<sortedWords.length-1; i++) {
if (sortedWords[i+1] == sortedWords[i]) {
duplicateWords.push(sortedWords[i]);
}
}
duplicateWords = $.unique(duplicateWords);
Thanks for any advice,
Use reduce to get the duplicate words, then you can iterate over tds to check in the text in it is present
within the duplicate words Array. If yes, then wrap the the text in mark tag.
const tds = document.querySelectorAll('td');
const groupByOccurence = [...tds].reduce((accu, td) => {
const textArr = td.innerHTML.split('<br>').map((word) => word.trim()).filter((word) => word.length > 0 && word.match(/[a-zA-Z]+/g));
textArr.forEach((text) => {
accu[text] = (accu[text] || 0) + 1;
});
return accu;
}, {});
const duplicateWords = Object.entries(groupByOccurence).filter(([_, val]) => val > 1).map(([key, _]) => key);
tds.forEach((td) => {
const textArr = td.innerHTML.split('<br>').map((word) => word.trim());
let str = "";
textArr.forEach((text) => {
if (duplicateWords.includes(text)) {
str += '<mark>' + text + '</mark><br>';
} else {
str += text + '<br>';
}
td.innerHTML = str;
})
});
const trs = document.querySelectorAll('tr');
trs.forEach((tr, i) => {
const specialChartds = [...tr.querySelectorAll('td')].filter((td) => !td.textContent.match(/[a-zA-Z]+/g));
if (!specialChartds) {
tr.append(tds[i]);
}
});
<table>
<tr>
<td>
order:<br /> TV
<br /> PS3 <br /> Laptop
</td>
<td>
qty:<br /> 1
<br /> 2
<br /> 1
<br />
</td>
</tr>
<tr>
<td>
----------------
</td>
<td>
----------------
</td>
</tr>
<tr>
<td>
recieved:<br /> TV <br /> Desktop
<br />
</td>
</tr>
</table>
To achieve expected result, use below option
Getting all words from table by looping $('table') with each
Creating array with all duplicate words from step1 using filter method
Looping all tds with each and adding mark tag for duplicate words
var text = $('table');
var arr = [];
//Step 1: Getting All words from table
var words = text.each(function(){
let val = $(this).text().replace(/\n|\r/g,' ').split(' ').filter(Boolean);
arr.push(...val)
})
//Step 2: Finding duplicate words
let duplicate = arr.filter(function(value,index,self){ return (self.indexOf(value) !== index && isNaN(parseInt(value)) && value.match(/[A-Za-z]/) !== null)})
//Step 3: Marking duplicate words in each row
$('td').each(function(){
let val = $(this).text();
let openMark = '<mark>'
let closeMark = '</mark>'
duplicate.forEach(v => {
if(val.indexOf(v) !== -1){
var html = $(this).html().replace(v, openMark + v + closeMark)
$(this).html(html)
}
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td>
order:<br />
TV<br />
PS3 <br />
Laptop
</td>
<td>
qty:<br />
1<br />
2<br />
1<br />
</td>
</tr>
<tr>
<td>
----------------
</td>
<td>
----------------
</td>
</tr>
<tr>
<td>
recieved:<br />
TV<br />
Desktop<br />
</td>
</tr>
</table>
codepen - https://codepen.io/nagasai/pen/YoPPMv?editors=1010
Related
I am trying to get the date and department info stored into an array, when a user inputs a value into my HTML. When I look at the console, I see it is being saved as E.fn.init. Here is my HTML code:
<form>
<p><b>Date:</b><input id="date_out" type='date' required /></p>
<p><b>Department:</b><input id='department_out' type='text' required /></p>
<button class="btn btn-primary" type="submit" id='submit' >SUBMIT</button>
</form>
And here is my Javascript Code:
let count = 1;
// Handler to Submit Data
$('#submit').click(() =>{
$('form').on('submit', function(e){
e.preventDefault();
const date_out = $("#date_out").val();
const department_out = $("#department_out").val();
let data = [];
// Iterate over all rows and store data
for (let i = 1; i <= count; i++){
// Skip Row if it was Removed
if (!$(`tr[index=${i}]`).length) continue;
// Store all Info from this row
let assetInfo = {
date_out: $(`#date_out${i}`).val(date_out),
department_out: $(`#department_out${i}`).val(department_out),
}
data.push(assetInfo);
console.log(data);
}
});
});
And the console prints the array as; date_out: E.fn.init, department_out: E.fn.init. How do I get it to save whatever the user inputs in the array?
I am not quite sure what is your HTML structure but as I understand, you have a table and you want to store each row in a data,
here is how I advise you to do it:
In my Table called depTable, I have each row with a unique ID like this:
date_out_1
department_out_1
so, when I want to access that, I just want to create that ID which is an easy task while I can get how many rows I have in that table, like this:
// Get ROW COUNT FROM TABLE
var count = $('#depTable tr').length;
Now, if you combine in For loop you will get all IDs
for (let i = 1; i <= count; i++){
var idDate = "#date_out_" + i;
var idDep = "#department_out_" + i;
}
here is my all code, hope I helped to solve your problem.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stack21</title>
<script src="js/jquery-3.6.0.min.js"></script>
</head>
<body>
<table id="depTable">
<thead>
<tr>
<td>Date</td>
<td>Department</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="date_out_1" type='date' required />
</td>
<td>
<input id='department_out_1' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_2" type='date' required />
</td>
<td>
<input id='department_out_2' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_3" type='date' required />
</td>
<td>
<input id='department_out_3' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_4" type='date' required />
</td>
<td>
<input id='department_out_4' type='text' required />
</td>
</tr>
</tbody>
</table>
<Button onclick="storeUserData()">Test me</Button>
<script>
let data = [];
function storeUserData(){
// Get ROW COUNT FROM TABLE
var count = $('#depTable tr').length;
console.log("Row Count: " + count)
for (let i = 1; i <= count; i++){
var idDate = "#date_out_" + i;
var idDep = "#department_out_" + i;
let assetInfo = {
date_out: $(idDate).val(),
department_out: $(idDep).val()
}
data.push(assetInfo);
}
console.log(data);
}
</script>
</body>
</html>
Demo code here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stack21</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- MY OFFLINE <script src="js/jquery-3.6.0.min.js"></script> -->
</head>
<body>
<table id="depTable">
<thead>
<tr>
<td>Date</td>
<td>Department</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="date_out_1" type='date' required />
</td>
<td>
<input id='department_out_1' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_2" type='date' required />
</td>
<td>
<input id='department_out_2' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_3" type='date' required />
</td>
<td>
<input id='department_out_3' type='text' required />
</td>
</tr>
<tr>
<td>
<input id="date_out_4" type='date' required />
</td>
<td>
<input id='department_out_4' type='text' required />
</td>
</tr>
</tbody>
</table>
<Button onclick="storeUserData()">Test me</Button>
<script>
let data = [];
function storeUserData(){
// Get ROW COUNT FROM TABLE
var count = $('#depTable tr').length;
console.log("Row Count: " + count)
for (let i = 1; i <= count; i++){
var idDate = "#date_out_" + i;
var idDep = "#department_out_" + i;
let assetInfo = {
date_out: $(idDate).val(),
department_out: $(idDep).val()
}
data.push(assetInfo);
}
console.log(data);
}
</script>
</body>
</html>
I'm looking to update an array like this :
// JSX in Render
<Table size="sm" responsive striped bordered hover>
<tbody>
<tr key={-1} className="defTabCra">
<th>Date</th>
<th>Morning</th>
<th>Afternoon travaillée</th>
</tr>
{this.generateMonth()}
</tbody>
</Table>
My function generateMonth() :
generateMonth = () => {
return MyArrayOfMomentJs.map((item,i) => { // Item is a momentJS object
var jour = item.format("ddd");
jour = jour.charAt(0).toUpperCase() + jour.slice(1);
if (item.isoWeekday() > 5 || item.CheckIfholiday()) {
return (
<tr key={i} className="NotWorked">
<th>{jour + ' ' + item.format("D")}</th>
<td />
<td />
</tr>
);
}
else {
var rowContainer = [];
//Morning
if (ArrayOfBooleanForTheMorning[i] !== null) { //null means that no choices has been made
if (ArrayOfBooleanForTheMorning[i]) {
rowContainer.push(
<td key={i}>
<input type="checkbox" value="true" />
<MaterialIcon color="green" icon="check" />
</td>
);
}
else rowContainer.push(
<td key={i}>
<input type="checkbox" value="false" />
<MaterialIcon icon="close" color="red" />
</td>
);
}
else rowContainer.push(<td key={i}>
<input type="checkbox" />
<MaterialIcon icon="remove" />
</td>);
//Afternoon
if (ArrayOfBooleanForTheAfternoon[i] !== null) {
if (ArrayOfBooleanForTheAfternoon[i])
rowContainer.push(
<td key={i + 31}>
<input type="checkbox" value="true" />
<MaterialIcon color="green" icon="check" />
</td>
);
else rowContainer.push (
<td key={i + 31}>
<input type="checkbox" value="false" />
<MaterialIcon icon="close" color="red" />
</td>
);
}
else rowContainer.push(<td key={i+31}> // If null
<input type="checkbox" />
<MaterialIcon icon="remove" />
</td>);
var row = [<tr key={i}><th>{jour + ' ' + item.format("D")}</th>{rowContainer}</tr>];
return row;
}
}, this);
}
The goal is quite simple : Everytime I make a change in the ArrayOfBooleanForTheAfternoon or ArrayOfBooleanForTheMorning, I would like to re-render the component.
And now things get strange (or maybe not, you guys will tell me :p) : The function is called everytime I make a change, which is good. The map function returns an array of JSX and the content of is good aswell. However, this new array doesn't replace the actual array already rendered.
I tried to put my jsx array in a state, to put directly my funtion in the code. Nothing works.
Anyone has any idea of what is going on ?
PS: I'm using bootstrap-react for the style of the page and his layout.
Wrong usage of key prop looks to be the likely issue.
Do you have something unique in the item object.You could use that.
Instead of
<td key={i}...>
try
<td key = {item.id} ...>
where id is the key for the unique entity in the item object.
Same for the tr tag.
I have a button that the user clicks on to add a new row to the bottom of an input table. I would like this to also increment the id. So the next row would have desc2, hours2, rate2 and amount2 as the id. Is there a way to do this in the JavaScript function.
Also - just want to check my logic on this. After the user completes the filled out form, I will be writing all the data to a mysql database on two different tables. Is this the best way to go about this? I want the user to be able to add as many lines in the desc_table as they need. If this is the correct way to be going about this, what is the best way to determine how many lines they have added so I can insert into the db table using a while loop?
JS file:
function new_line() {
var t = document.getElementById("desc_table");
var rows = t.getElementsByTagName("tr");
var r = rows[rows.length - 1];
var x = rows[1].cloneNode(true);
x.style.display = "";
r.parentNode.insertBefore(x, r);
}
HTML:
<table id="desc_table">
<tr>
<td><font><br><h3>Description</h3></font></td>
<td><font><h3>Hours</h3></font></td>
<td><font><h3>Rate</h3></font></td>
<td><font><h3>Amount</h3></font></td>
<td></td>
</tr>
<tr>
<td ><textarea name="description" id="desc1" ></textarea></td>
<td> <input type="text" name="hours" id="hours1" ></td>
<td> <input type="text" name="rate" id="rate1"></td>
<td><input type="text" name="amount" id="amount1"></td>
<td>
<button type="button" name="add_btn" onclick="new_line(this)">+</button>
<button type="button" name="delete_btn" onclick="delete_row(this)">x</button>
</td>
</tr>
</table>
Thank you!
Check this code.After appending the row it counts the number of rows and and then assigns via if condition and incremental procedure the id's:
function new_line() {
var t = document.getElementById("desc_table");
var rows = t.getElementsByTagName("tr");
var r = rows[rows.length - 1];
var x = rows[1].cloneNode(true);
x.style.display = "";
r.parentNode.insertBefore(x, r);
for(var i=1;i<rows.length;i++){
if(rows[i].children["0"].children["0"].id.match((/desc/g))){
rows[i].children["0"].children["0"].id='desc'+i;
}
if(rows[i].children["1"].children["0"].id.match((/hours/g))){
rows[i].children["1"].children["0"].id='hours'+i;
}
if(rows[i].children["2"].children["0"].id.match((/rate/g))){
rows[i].children["2"].children["0"].id='rate'+i;
}
if(rows[i].children["3"].children["0"].id.match((/amount/g))){
rows[i].children["3"].children["0"].id='amount'+i;
}
}
}
<table id="desc_table">
<tr>
<td><font><br><h3>Description</h3></font></td>
<td><font><h3>Hours</h3></font></td>
<td><font><h3>Rate</h3></font></td>
<td><font><h3>Amount</h3></font></td>
<td></td>
</tr>
<tr>
<td ><textarea name="description" id="desc1" ></textarea></td>
<td> <input type="text" name="hours" id="hours1" ></td>
<td> <input type="text" name="rate" id="rate1"></td>
<td><input type="text" name="amount" id="amount1"></td>
<td>
<button type="button" name="add_btn" onclick="new_line(this)">+</button>
<button type="button" name="delete_btn" onclick="delete_row(this)">x</button>
</td>
</tr>
</table>
Please change variable names for more descriptive. :)
Example solution...
https://jsfiddle.net/Platonow/07ckv5u7/1/
function new_line() {
var table = document.getElementById("desc_table");
var rows = table.getElementsByTagName("tr");
var row = rows[rows.length - 1];
var newRow = rows[rows.length - 1].cloneNode(true);
var inputs = newRow.getElementsByTagName("input");
for(let i=0; i<inputs.length; i++) {
inputs[i].id = inputs[i].name + rows.length;
}
var textarea = newRow.getElementsByTagName("textarea")[0];
textarea.id = textarea.name + rows.length;
table.appendChild(newRow);
}
Note that I removed/edited below fragment.
x.style.display = "";
r.parentNode.insertBefore(x, r);
You could do this a lot easier with jquery or another dom manipulation language, but with vanilla JS here's an example of simply looping through the new row's inputs & textarea and incrementing a counter to append.
var count = 1;
function new_line() {
count++;
var t = document.getElementById("desc_table");
var rows = t.getElementsByTagName("tr");
var r = rows[rows.length - 1];
var x = rows[1].cloneNode(true);
x.style.display = "";
r.parentNode.insertBefore(x, r);
// update input ids
var newInputs = Array.from(x.getElementsByTagName('input'))
.concat(Array.from(x.getElementsByTagName('textarea')));
newInputs.forEach(function(input) {
var id = input.getAttribute('id').replace(/[0-9].*/, '');
input.setAttribute('id', id + count);
});
}
<table id="desc_table">
<tr>
<td><font><br><h3>Description</h3></font></td>
<td><font><h3>Hours</h3></font></td>
<td><font><h3>Rate</h3></font></td>
<td><font><h3>Amount</h3></font></td>
<td></td>
</tr>
<tr>
<td ><textarea name="description" id="desc1" ></textarea></td>
<td> <input type="text" name="hours" id="hours1" ></td>
<td> <input type="text" name="rate" id="rate1"></td>
<td><input type="text" name="amount" id="amount1"></td>
<td>
<button type="button" name="add_btn" onclick="new_line(this)">+</button>
<button type="button" name="delete_btn" onclick="delete_row(this)">x</button>
</td>
</tr>
</table>
I was only able to insert a "$" sign in a3. I also need to insert the "$" sign for a1 but when I insert format for a1 field in like this
$('#a3').val(format($('#a1').val(format) * $('#a2').val()));
I get an error. What is the best way to do this?
HTML
<table class="table">
<tbody>
<tr>
<td>You</td>
<td class="light-gray">Example</td>
</tr>
<tr>
<td width="20%">a1
<input type="textbox" class="form-control" id="a1" name="a1" />
</td>
<td width="20%" class="light-gray">$30,000</td>
</tr>
<tr>
<td>a2
<input type="text" class="form-control" id="a2" name="a2" />
</td>
<td class="light-gray">90%</td>
</tr>
<tr>
<td>a3
<input type="text" class="form-control" id="a3" name="a3" data-cell="a3" />
</td>
<td class="light-gray">$27,000</td>
</tr>
<tr class="heading">
<th colspan="2"> </th>
</tr>
</table>
jQuery
$(document).ready(function () {
var format = function (num) {
var str = num.toString().replace("$", ""),
parts = false,
output = [],
i = 1,
formatted = null;
if (str.indexOf(".") > 0) {
parts = str.split(".");
str = parts[0];
}
str = str.split("").reverse();
for (var j = 0, len = str.length; j < len; j++) {
if (str[j] != ",") {
output.push(str[j]);
if (i % 3 == 0 && j < (len - 1)) {
output.push(",");
}
i++;
}
}
formatted = output.reverse().join("");
return ("$" + formatted + ((parts) ? "." + parts[1].substr(0, 2) : ""));
};
$("#a1,#a2").keyup(function (e) {
$('#a3').val(format($('#a1').val() * $('#a2').val()));
});
});
JSFiddle Examples
Your problem is that you assume the values inside the inputs to be numbers. Since you format those with non-digit-characters they are not (and can not be casted to a number).
$('#a1').val() * $('#a2').val()
Since the values you are multiplying are no numbers an error is thrown.
To be able to multiply those strings (or to be exact the value they are representing) you need to unformat your strings before you can cast those to numbers and do your calculation!
You'll find an working example at https://jsfiddle.net/hn669das/18/.
By the way: Better use the change-event for formatting instead of the keyUp-event since the user won't be able to write into the input undisturbed when the input is manipulated while the user intends to write to it. The change-event will trigger when the input looses its focus and so won't disturb the user.
I hope that helps you!
I have n amount of items (html elements, vcard microformat), containing other elements with contents, each element represents a store and has a child element (".locality") where is kept information about a city, where the store is.
I want to make a section for every original city, output city´s name as a heading of section and order each item under correct section.
This is where I have gone so far:
http://jsfiddle.net/eliasondrej/0zqhpfLn/1/
HTML:
<table class="vcard">
<tr class="info">
<td>
<h3 class="fn org">some text </h3>
</td>
<td>
some text
</td>
<td>
<p class="addr">
<span class="locality">New York </span> <br />
</p>
</td>
</tr>
</table>
<table class="vcard">
<tr class="info">
<td>
<h3 class="fn org">some text </h3>
</td>
<td>
some text
</td>
<td>
<p class="addr">
<span class="locality">Paris </span> <br />
</p>
</td>
</tr>
</table>
<table class="vcard">
<tr class="info">
<td>
<h3 class="fn org">some text </h3>
</td>
<td>
some text
</td>
<td>
<p class="addr">
<span class="locality">Rome </span> <br />
</p>
</td>
</tr>
</table>
<table class="vcard">
<tr class="info">
<td>
<h3 class="fn org">some text </h3>
</td>
<td>
some text
</td>
<td>
<p class="addr">
<span class="locality">Berlin </span> <br />
</p>
</td>
</tr>
</table>
</div>
jQuery:
jQuery(document).ready(function() {
var prodejny = [];
var localityTexts = [];
jQuery(".vcard .locality").each(function() {
localityTexts.push(jQuery(this).text() );
});
jQuery(".vcard").each(function() {
prodejny.push(jQuery(this).html() );
});
var localities = [];
for (var i=0; i<localityTexts.length; i++) {
var locality = localityTexts[i];
if (localities.indexOf(locality) === -1) {
localities.push(locality);
}
}
localities.sort();
for (i = 0; i < localities.length;i++) {
jQuery('.item-page').append( '<div class="lokalita"><h2> ' + localities[i] + '</h2><div class="prodejny-v-lokalite"> </div></div>');
}
});
I don´t know how to campare Item´s location with that in section heading and then order it to their section.
Thank you much for any advice.
As I can click your trouble, you should use objects instead of lots of Arrays. Be more functionality.
Something like this:
var $page = $('.item-page');
$('.vcard').map(function (i, el) {
var $vcard = $(el);
return {
label: $.trim($vcard.find('.locality').text()),
order: i + 1 // Coz we start count from 0
};
}).sort(function (a, b) {
return a.label > b.label;
}).each(function (i, locality) {
$page.append('<div class="lokalita"><h2> ' + locality.label + '</h2><div class="prodejny-v-lokalite">'+ locality.order + '</div></div>');
});
And link to result:
http://jsfiddle.net/99oup27k/