JS/jQuery: Copying Contents of td to clipboard - javascript

I'm working on a project where I have a table full of first names, last names, and e-mail addresses. The last td should be a button that allows the user to copy that particular person's e-mail address to the clipboard.
Also yes, I'm aware this is in old-school JS, I'm working on a legacy project.
Here's my code on codepen.io: https://codepen.io/anfperez/pen/ZZdwWL
HTML
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>E-mail</th>
<th>Button</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td id="email">jsmith#whatever.com</td>
<td><button>Click to Copy</button></td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td id="email">ejackson#whatever.com</td>
<td><button>Click to Copy</button></td>
</tr>
</table>
JS
function copyToClipboard() {
var copyText = document.getElementById("email")
copyText.select();
document.execCommand("copy");
alert("Copied the text: " + copyText.value);
}
So, I have two dilemmas:
1) how can I get each button generated to copy the correct e-mail address (not just one or all of them)? I need to assign unique IDs to each entry it seems, but I don't really know how to start generating those if the list gets longer.
2) I keep getting the error that "copyText.select() is not a valid function". I've been following several tutorials in which this method is used, so I'm not sure why it's not working here.

As Robin Zigmond says, you need to change id="email" to class="email" to be able to find the correct TD, and because each id must be unique.
Once you have done that, you can add an event listener to each button programmatically, and within the listener find the email TD with the email classname.
Selecting text only works in elements that can have text input (i.e. textarea and input type="text"), so you need to create a temporary element to put the text into, and copy it from there.
(function()
{
let buttons = document.getElementsByTagName('Button');
for(let i = 0; i < buttons.length; i++)
{
let button = buttons[i];
button.addEventListener('click', e =>
{
let button = e.target;
let email = button.parentNode.parentNode.getElementsByClassName('email')[0].innerHTML;
let text = document.createElement('input');
text.setAttribute('type', 'text');
text.value = email;
document.body.appendChild(text);
text.select();
document.execCommand('copy');
document.body.removeChild(text);
});
}
})();
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>E-mail</th>
<th>Button</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td class="email">jsmith#whatever.com</td>
<td><button>Click to Copy</button></td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td class="email">ejackson#whatever.com</td>
<td><button>Click to Copy</button></td>
</tr>
</table>

I have modified your Codepen code.
Here is a working example.
document.querySelectorAll('button[data-type="copy"]')
.forEach(function(button){
button.addEventListener('click', function(){
let email = this.parentNode.parentNode
.querySelector('td[data-type="email"]')
.innerText;
let tmp = document.createElement('textarea');
tmp.value = email;
tmp.setAttribute('readonly', '');
tmp.style.position = 'absolute';
tmp.style.left = '-9999px';
document.body.appendChild(tmp);
tmp.select();
document.execCommand('copy');
document.body.removeChild(tmp);
console.log(`${email} copied.`);
});
});
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>E-mail</th>
<th>Button</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td data-type="email">jsmith#whatever.com</td>
<td><button data-type="copy">Click to Copy</button></td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td data-type="email">ejackson#whatever.com</td>
<td><button data-type="copy">Click to Copy</button></td>
</tr>
<tr>
<td>Eve1</td>
<td>Jackso1n</td>
<td data-type="email">ejackssdfon#whafdstever.com</td>
<td><button data-type="copy">Click to Copy</button></td>
</tr>
<tr>
<td>Eve2</td>
<td>Jackson2</td>
<td data-type="email">asdas#whasdftever.com</td>
<td><button data-type="copy">Click to Copy</button></td>
</tr>
</table>
This source can be helpful as well source

Related

Cannot style elements using DOM manipulation when new class is added by element.classList.add() method in javascript

I am trying to style a table like this
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td >Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td >Jackson</td>
<td>94</td>
</tr>
</table>
which has a Javascript code that would create classes shown below
td.classList.add("col");
However I don't understand why styling using that class won't work with the below code
const tabl= document.getElementsByClassName("col");
for (var i = 0; i < tabl.length; i++) {
tabl[i].style.backgroundColor="red";
}
So, I can't see where you're going wrong from the little code you presented. I filled in the gaps as best I could to get this working, and it adds the red color for me.
//Get NodeList of all tds
let tds = document.querySelectorAll('td');
//Convert NodeList to Array
tds = Array.from(tds);
//Add class 'col' to each td
tds.map((td) => td.classList.add('col'));
//Get all '.col' elements
const tabl= document.getElementsByClassName("col");
//Programmatically set style attribute on each .col
for (var i = 0; i < tabl.length; i++) {
tabl[i].style.backgroundColor="red";
}
It's generally considered bad practice to directly, programmatically set the style of an element in this way; it's better to add a class and handle style via the CSS file. Since you're already adding a class, why not just set that class to set background-color: red in the CSS file anyway?
Anyway, you can check a StackBlitz here to see it up and running: https://stackblitz.com/edit/js-2zm68n
Why use class name? Please try this. Hope it can help
var elements = document.getElementsByTagName('td')
var tds = Array.from(elements)
tds.map(element => {
element.setAttribute('class', 'classname')
})
// check
console.log('finded tds: ' + document.getElementsByClassName('classname').length)
<table>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td >Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td >Jackson</td>
<td>94</td>
</tr>
</table>

How to get multiple selected cell array values with checkbox in jquery, then send with ajax post

How should I get an array value from a table cell when clicking checkbox with jQuery? If I've selected cell 1, I want to get array like ["BlackBerry Bold", "2/5", "UK"], but if I've selected all of them, I want to get all the data in the form of an array of arrays.
<table border="1">
<tr>
<th><input type="checkbox" /></th>
<th>Cell phone</th>
<th>Rating</th>
<th>Location</th>
</tr>
<tr>
<td align="center"><input type="checkbox"/></td>
<td>BlackBerry Bold 9650</td>
<td>2/5</td>
<td>UK</td>
</tr>
<tr>
<td align="center"><input type="checkbox" /></td>
<td>Samsung Galaxy</td>
<td>3.5/5</td>
<td>US</td>
</tr>
<tr>
<td align="center"><input type="checkbox"/></td>
<td>Droid X</td>
<td>4.5/5</td>
<td>REB</td>
</tr>
Please help.
Onclick get 3 children of the parent and add content to data. Used jquery nextAll for siblings and splice the 3 required.
Attached event to the table, onclick will check if element is INPUT.
If it's input, will get parent of that input which will be <td>.
For this parent element, will get three siblings using jquery.
Will add in selected if not present else delete, using indexOf.
CodePen for you to playaround: [ https://codepen.io/vivekamin/pen/oQMeXV ]
let selectedData = []
let para = document.getElementById("selectedData");
let tableElem = document.getElementById("table");
tableElem.addEventListener("click", function(e) {
if(e.target.tagName === 'INPUT' ){
let parent = e.target.parentNode;
let data = [];
$(parent).nextAll().map(function(index, node){
data.push(node.textContent);
})
let index = selectedData.indexOf(JSON.stringify(data))
if(index == -1){
selectedData.push(JSON.stringify(data));
}
else{
selectedData.splice(index,1);
}
para.textContent = "";
para.innerHTML = selectedData ;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1" id="table">
<tr>
<th><input type="checkbox" /></th>
<th>Cell phone</th>
<th>Rating</th>
<th>Location</th>
</tr>
<tr>
<td align="center"><input type="checkbox"/></td>
<td>BlackBerry Bold 9650</td>
<td>2/5</td>
<td>UK</td>
</tr>
<tr>
<td align="center"><input type="checkbox" /></td>
<td>Samsung Galaxy</td>
<td>3.5/5</td>
<td>US</td>
</tr>
<tr>
<td align="center"><input type="checkbox"/></td>
<td>Droid X</td>
<td>4.5/5</td>
<td>REB</td>
</tr>
</table>
<h3> Selected Data: </h3>
<p id="selectedData"></p>
Updated to meet your needs.
create a function to build the array values based on looking for any checked inputs then going to their parents and grabbing the sibling text values
attach your change event to the checkbox click even.
I provided a fiddle below that will output the array in the console.
function buildTheArray(){
var thearray = [];
$("input:checked").parent().siblings().each(function(){
thearray.push($(this).text());
});
return thearray;
}
$("input[type='checkbox']").change(function(){
console.log(buildTheArray());
});
Fiddle:
http://jsfiddle.net/gcu4L5p6/

textContent and innerHTML not changing the DOM

Good morning,
I'm hoping someone can help me with this seemingly simple question. I can't figure out why my textContent or innerHTML won't update my DOM. It shows in my console that it has changed, but for some reason that escapes me I can't figure out why the DOM isn't changing. Any help is appreciated!
I have the following code
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
grabtext = 'Super User';
}
console.log(grabtext);
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
innerHTML and textContent are basically setters and getters. Getting their reference in a variable and setting value to that reference will not invoke the setter.
You need to set value directly to them
e.textContent ?
( e.textContent == 'SUPER' ? (e.textContent = 'Super User') : "" ) :
( e.innerHTML == 'SUPER' ? (e.innerHTML = 'Super User') : "" )
I have updated the snippet.
You were not assigning the Super User value to e.textContent inside if condition
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
e.textContent = 'Super User';
}
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Hope this will help
Nothing in your code changes what's in the DOM. Assigning to your grabText variable just changes the value of that variable, not the element you got the text from.
To do that, you'd havE to assign back to textContent/innerHTML.
Separately: innerHTML is very different from textContent. If you're going to feature-detect, use textContent and fall back to innerText, not innerHTML¹. When feature-detecting this, don't just use ||, it can pick the wrong one if the element's text is blank. Instead, look to see if the element has a property with the desired name. You only need to do it once and remember it.
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Side note: You don't put ; after function declarations. It's a statement terminator; declarations aren't statements. You'd have one after an assignment statement with a function expression, but not after a declaration. (It's harmless, though.)
¹ textContent and innerText are also different from one another, but the differences don't matter in your case.

Javascript function not working

My jsp page contains a table ,the code for the same is given below:
<table width="400" border="1" cellpadding="0" cellspacing="1" id="student_table">
<thead>
<tr>
<th scope="row">ID</th>
<th scope="row">Name</th>
<th scope="row">Country</th>
<th scope="row">Marks</th>
<th scope="row">Rank</th>
</tr>
<tbody>
<tr>
<td>1</td>
<td>Smith</td>
<td>US</td>
<td><input type="text" name="marks" value="40"/></td>
<td>20</td>
</tr>
<tr>
<td>2</td>
<td>John</td>
<td>England</td>
<td><input type="text" name="marks" value="80"/></td>
<td>29</td>
</tr>
<tr>
<td>3</td>
<td>William</td>
<td>Australia</td>
<td><input type="text" id="nm" name="marks" value="60" onblur="return(myFunction1())"/></td>
<td>33</td>
</tr>
<tr>
<td>4</td>
<td>Michael</td>
<td>Germany</td>
<td><input type="text" name="marks" value="90"/></td>
<td>29</td>
</tr>
I have a javascript function which compares the values in two cells. But the javascript function is not working. I cannot find out why. Please anyone help me with a solution. I know that there are other ways to validate. but i need to get it done this way. This is an example of a big program which i need to get done in this way. Please help
function myfunction11(){
var myTable = document.getElementById('student_table').tBodies[0];
// first loop for each row
for (var r=0, n = myTable.rows.length; r < n; r++) {
// this loop is getting each colomn/cells
for (var c = 0, m = myTable.rows[r].cells.length; c < m; c++) {
if(myTable.rows[r].cells[c].childNodes[0].value){
var rank = myTable.rows[r].cells[4].innerText;
var marks = myTable.rows[r].cells[c].childNodes[0].value;
if(rank>marks){
alert("rank cannot be greater than marks:"+marks);
myTable.rows[r].cells[c].childNodes[0].value="0";
return false;
}
}
}
}
return true;
}
In your HTML, you have:
onblur="return(myFunction1())
but your actual function's name is:
myfunction11()
Once the names are matched, your function runs. But, you do have one (at least) issue with your code. You are comparing rank > marks but marks comes from an input field. All HTML data is strings, so you must convert that string to a number to do any kind of mathematical operation on it. Also, innerText is non-standard code, use textContent instead. See comments in code.
function myFunction1(){
var myTable = document.getElementById('student_table').tBodies[0];
// first loop for each row
for (var r=0, n = myTable.rows.length; r < n; r++) {
// this loop is getting each colomn/cells
for (var c = 0, m = myTable.rows[r].cells.length; c < m; c++) {
if(myTable.rows[r].cells[c].childNodes[0].value){
// All HTML data is strings. If you expect a number, you have to convert it.
// Also, use textContent to get the text of an element. innerText is non-standard
var rank = parseInt(myTable.rows[r].cells[4].textContent,10);
var marks = parseInt(myTable.rows[r].cells[c].childNodes[0].value, 10);
if(rank > marks){
alert("rank cannot be greater than marks: " + marks);
myTable.rows[r].cells[c].childNodes[0].value = "0";
return false;
}
}
}
}
return true;
}
<table width="400" border="1" cellpadding="0" cellspacing="1" id="student_table">
<thead>
<tr>
<th scope="row">ID</th>
<th scope="row">Name</th>
<th scope="row">Country</th>
<th scope="row">Marks</th>
<th scope="row">Rank</th>
</tr>
<tbody>
<tr>
<td>1</td>
<td>Smith</td>
<td>US</td>
<td><input type="text" name="marks" value="40"/></td>
<td>20</td>
</tr>
<tr>
<td>2</td>
<td>John</td>
<td>England</td>
<td><input type="text" name="marks" value="80"/></td>
<td>29</td>
</tr>
<tr>
<td>3</td>
<td>William</td>
<td>Australia</td>
<td><input type="text" id="nm" name="marks" value="60" onblur="return(myFunction1())"/></td>
<td>33</td>
</tr>
<tr>
<td>4</td>
<td>Michael</td>
<td>Germany</td>
<td><input type="text" name="marks" value="90"/></td>
<td>29</td>
</tr>
</table>
Now, to correct your code and have it use modern standards so that it works when you leave any of the fields, we'd write:
// Don't use inline HTML event handling attributes like "onclick", "onblur", etc.
// Instead, use modern standards of separating all your JavaScript from your HTML
// Get a collection of all the input fields. There are many ways to do this, but here
// we are getting all the elements that use the marks class (HTML adjusted above)
var inputs = document.querySelectorAll(".marks");
// Loop through the collection and assign the checkMarks function as the blur event
// callback funciton to each of them.
for(var i = 0; i < inputs.length; i++){
inputs[i].addEventListener("blur", checkMarks);
}
function checkMarks(evt){
// Just check the marks and the rank next to it
// All HTML data is strings. If you expect a number, you have to convert it.
// The parseInt() function can extract numbers from a string.
// Also, use textContent to get the text of an element. innerText is non-standard
// All event handling functions automatically recieve an argument representing the
// event that they are responding to (evt in this case). That event object, in turn,
// has a property (target) that references the element that triggered the event in the
// first place. To get to the table cell that comes after an input field, we start at
// the input field (evt.target) and then get the parent element of that (the <td> element
// that the input is inside of) and then the next element that is a sibling of that (the <td>
// that contains the rank.
var rank = parseInt(evt.target.parentNode.nextElementSibling.textContent, 10);
// To get the value of the input, just look at evt.target's value
var marks = parseInt(evt.target.value, 10);
if(rank > marks){
alert("rank cannot be greater than marks: " + marks);
evt.target.value = "0";
}
}
<table width="400" border="1" cellpadding="0" cellspacing="1" id="student_table">
<thead>
<tr>
<th scope="row">ID</th>
<th scope="row">Name</th>
<th scope="row">Country</th>
<th scope="row">Marks</th>
<th scope="row">Rank</th>
</tr>
<tbody>
<!-- Form elements should generally have unique names so you can tell them apart when
they submit their data. -->
<tr>
<td>1</td>
<td>Smith</td>
<td>US</td>
<td><input type="text" class="marks" name="US_Marks" value="40"/></td>
<td>20</td>
</tr>
<tr>
<td>2</td>
<td>John</td>
<td>England</td>
<td><input type="text" class="marks" name="England_Marks" value="80"/></td>
<td>29</td>
</tr>
<tr>
<td>3</td>
<td>William</td>
<td>Australia</td>
<td><input type="text" id="nm" class="marks" name="Austrailia_Marks" value="60"></td>
<td>33</td>
</tr>
<tr>
<td>4</td>
<td>Michael</td>
<td>Germany</td>
<td><input type="text" class="marks" name="Germany_Marks" value="90"/></td>
<td>29</td>
</tr>
</table>
try changing onblur="return(myFunction1())" to onblur="return(myFunction11())"

HTML editable Table -> get edited Fields

I have a HTML-Table
<table style="width:100%">
<tr>
<th>id</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>2</td>
<td contenteditable="true">Smith</td>
<td contenteditable="true">50</td>
</tr>
<tr>
<td>3</td>
<td contenteditable="true">Jackson</td>
<td contenteditable="true">94</td>
</tr>
</table>
(Just a TEST-Table)
It is editable, but how do I get all the rows (with ID) which were edited, to send them to a PHP-Backend which saves the changes to DB?
Thanks in advance,
Patrick.
You can save ids in an Array whenever field content is changed.
Here is the Working Example: https://jsfiddle.net/79egs9tc/
var idArr = [];
$(".edited").focusout(function() {
var id = $(this).parent().attr('id');
if($.inArray(id, idArr) === -1){
idArr.push(id);
}
console.log(idArr);
});
You can add check for content is changes or not.
Hope, it will work for you.

Categories

Resources