Use greasemonkey to add HTML before table - javascript

I am using greasemonkey to edit a page. I need to add my own table between the two tables that are already on the page and then remove the second table. There is nothing really setting the two existing tables apart, so I am having trouble with the function to insertBefore.
<h3>Table 1</h3>
<table class="details" border="1" cellpadding="0" cellspacing="0">
<tbody><tr>
<th>1</th>
<td>2</td>
</tr>
</tbody></table>
<h3>Table 2</h3>
<table class="details" border="1">
<tbody><tr>
<th>1</th>
<td>2</td>
</tr><tr>
<th>3</th>
<td>4</td>
</tr>
</tbody></table>
I have found the below code helpful in removing table 2, but I need to add my own table before table 2 first:
// find second <table> on this page
var xpathResult = document.evaluate('(//table[#class="details"])[2]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var node=xpathResult.singleNodeValue;
// now hide it :)
node.style.display='none';

This is a good chance to introduce jQuery. jQuery will be dead useful for the other things your GM script will do, plus, it's robust and cross-browser capable (for reusing your code).
(1) Add this line to the Greasemonkey metadata section, after the // #include directive(s):
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
(Note you may have to uninstall and then reinstall the script to get jQuery copied over.)
(2) Then you can use this code to add your table and delete the old one:
//--- Get the 2nd table with class "details".
var jSecondTable = $("table.details:eq(1)");
//--- Insert my table before it.
jSecondTable.before
(
'<table id="myTable">'
+ ' <tr>'
+ ' <th></th>'
+ ' <th></th>'
+ ' </tr>'
+ ' <tr>'
+ ' <td></td>'
+ ' <td></td>'
+ ' </tr>'
+ '</table>'
);
//--- Delete the undesired table.
jSecondTable.remove ();
/*--- Alternately, just hide the undesired table.
jSecondTable.hide ();
*/
You can see a version of this code, in action, at jsFiddle.
Alternate method of adding your table -- Less straightforward but does not require all the quotes:
jSecondTable.before ( (<><![CDATA[
<table id="myTable">
<tr>
<th></th>
<th></th>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
]]></>).toString ()
);

Related

Split one big table into multiple tables based on content of acolumn in each row

I looked at previous similar questions and only found one answer with the following code splitting the data into 2 tables:
// ==UserScript==
// #name TABLE SPLITTER
// #namespace http://www.w3schools.com/
// #description DESCRIPTION!!!
// #include http://www.w3schools.com/css/css_table.asp
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// #require https://raw.github.com/tomgrohl/jQuery-plugins/master/jcookie/script/jquery.jcookie.min.js
// #version 1
// ==/UserScript==
$(function(){
// YOUR JAVASCRIPT CODE HERE
// YOU HAVE JQUERY INCLUDED
setTimeout(function(){
var mainTable = $("table");
var splitBy = 3;
var rows = mainTable.find ( "tr" ).slice( splitBy );
var secondTable = $("<table id='secondTable' style='background:pink;'><tbody></tbody></table>").insertAfter("table");
secondTable.find("tbody").append(rows);
console.log(secondTable);
mainTable.find ( "tr" ).slice( splitBy ).remove();
}, 3000);
});
I am looking for something like this that will split the information to tables base on the amount of different options i have.
ultimately i would like something like:
Goal
Or even better remove the type from the output and have it show before each of the new tables like this: option 2
Not sure if that even possible and would love some help.
This is not the optimal solution, you can get the idea and improve it.
Read JS comments.
var dynamicData = $('#dynamicData'); // To identify the parent that we will append data to.
$(document).ready(function(){
$('.types').each(function(){ // loop on each type and check if that type not appended inside '#dynamicData' as 'h5', if no,t append it and append a table related to it
var name = $.trim($(this).text());
var check = $('h5#i_' + name , dynamicData).length;
if (check === 0){
$(dynamicData).append('<h5 id="i_' + name + '">' + name + '</h5>');
$(dynamicData).append('<table id="t_' + name + '" class="table table-hover table-striped table-bordered"></table>');
$('table#t_' + name).append('<thead>'+
'<tr>'+
'<th>Product</th>'+
'<th>Price</th>'+
'</tr>'+
'</thead>'+
'<tbody>'+
'</tbody>');
}
});
$('#allContent > tr').each(function(){ // loop on each row in '#allContent' and read '.types' class, then clone this row and remove the type then append it inside the target table using id
var name = $.trim($('.types',this).text());
$(this).clone().find('.types').remove().end().appendTo('table#t_' + name + ' > tbody');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<h4 class="text-center text-danger">Before:</h4>
<table class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Type</th>
</tr>
</thead>
<tbody id="allContent">
<tr>
<td>TV</td>
<td>250$</td>
<td class="types">Product</td>
</tr>
<tr>
<td>Channel</td>
<td>1$</td>
<td class="types">Service</td>
</tr>
<tr>
<td>Channel</td>
<td>1$</td>
<td class="types">Service</td>
</tr>
<tr>
<td>DVD</td>
<td>14$</td>
<td class="types">Product</td>
</tr>
<tr>
<td>Support</td>
<td>15$</td>
<td class="types">Team</td>
</tr>
</tbody>
</table>
<h4 class="text-center text-danger">After:</h4>
<div id="dynamicData"></div>
My first thought is make a unique list of the types. Then loop over that list, cloning the original table for each. Then loop through the cloned table and remove everything that you don't want there. Definitely not the most efficient, but it's simple and it works.
let types = [... new Set($('table.original tr td:last-of-type')
.get().map(type => type.textContent))];
//create a container for the cloned tables
$('table.original').after(`<h4>After:</h4><div class="cloned-tables"></div>`)
//loop over types, clone tables, modify accordingly
$.each(types, function(index, type) {
$(`<p class="type">${type}</p>${$('table.original')[0].outerHTML}`)
.appendTo('.cloned-tables')
.find('tr td:last-of-type').each(function() {
if (this.textContent !== type) { this.parentElement.remove(); }
this.remove();
});
});
//remove all type header cells
$(`.cloned-tables table th:last-of-type`).remove();
h4{color: red;}
.type{color: blue;}
<h4>Before:</h4>
<table class="original">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>TV</td>
<td>$250</td>
<td>Product</td>
</tr>
<tr>
<td>Channel</td>
<td>$1</td>
<td>Service</td>
</tr>
<tr>
<td>Channel</td>
<td>$1</td>
<td>Service</td>
</tr>
<tr>
<td>DVD</td>
<td>$14</td>
<td>Product</td>
</tr>
<tr>
<td>Support</td>
<td>$15</td>
<td>Team</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Another thought on using greasemonkey, make sure that the table exist and is populated before you try and do anything with it. Greasemonkey is in a different scope than the original code, so document.ready() is inaccurate. Sometimes things load very asychronously, which will make valid code seem broken. I tend to do something like this:
let loading = setInterval(function() {
if ($('table.original').length) {
clearInterval(loading);
//greasmonkey code here
}
}, 1000);

How to add listener to table column

I want to add listener to the html table column So that when I click the so called visualized table column, I want to perform something using js. consider this:
<table>
<thead>
<tr>
<th>name</th>
<th>lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>Guga</td>
<td>Nemsitsveridze</td>
</tr>
<tr>
<td>Giorgi</td>
<td>Beshidze</td>
</tr>
</tbody>
</table>
Now when I click the lastname cell (I mean the one of the following elements: <th>lastname</th>, <td>Nemsitsveridze</td>, <td>Beshidze</td>) I want to perform something using js.
The solution I was thinking about is to assign some kind of class attribute to each element of the lastname cell and add the same event listener to them independently, but I'm not sure if it is the only solution to this problem.
If anyone has an idea how to achieve this goal, please, answer the question.
Thanks in advance.
In order to execute something when a column is clicked, you can add one event listener to the whole table and then check which column was clicked, then execute some code.
Using window.event.target.cellIndex you can access the cell index.
Using window.event.target.parentNode.rowIndex you can access the row index.
document.getElementById('myTbl').addEventListener('click', function(event)
{
var col = window.event.target.cellIndex;
var row = window.event.target.parentNode.rowIndex;
if (col==1){
alert('Col index is: ' + col + '\nRow index is: ' + row);
}
});
table, td {
border: 1px solid black;
}
<table id="myTbl">
<thead>
<tr>
<th>name</th>
<th>lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>Guga</td>
<td>Nemsitsveridze</td>
</tr>
<tr>
<td>Giorgi</td>
<td>Beshidze</td>
</tr>
</tbody>
</table>

What is the most efficient way to reload CSS after adding data dynamically using jQuery?

This row was added after an ajax call:
<tr id="product1" class = "success">
<td>1</td>
<td>product one</td>
</tr>
the class success puts a green background to the row, but obviously this style is lost because the row was added dynamically.
I've seen solutions by dynamic loads of CSS, but I want to know which would be the most efficient if you get to have an extensive stylesheet.
thanks
i'm using boostrap:
<table id = "table_result" class="table">
<thead id ="table_result_search">
<tr>
<th>#</th>
<th>Product</th>
<th>Price</th>
<th>Stock</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
and Jquery:
//ajax
var tr = TrTableResult(id,nombre, stock, price, n);
$("#table_result_search").append(tr);
//fin ajax
function TrTableResult(id,nombre,stock,price,n){
var color = new Array("success", "error", "warning", "info");
var tr = '<tr id ="'+id+'" class="' + color[n] + '">';
tr+= '<td>'+ id +'</td>';
tr+= '<td>'+ product+'</td>';
tr+= '<td>'+ price +'</td>';
tr+= '<td>'+ stock +'</td>';
tr+= '</tr>';
return tr;
}
Updated answer:
Now that you've quoted your markup and code, it's clear that you do have the table class, so the original answer below isn't it.
But note that you're appending your tr to your thead element:
$("#table_result_search").append(tr);
Where your markup is:
<thead id ="table_result_search">
You're not seeing any effect of the success class because the rule is:
.table tbody tr.success > td {
background-color: #dff0d8;
}
Note the tbody in there. So the selector doesn't match, because you're appending the row to a thead, not a tbody.
Original answer:
As several of us have pointed out in the comments, CSS is applied by the browser to all elements that match the relevant selectors, whether added dynamically or not.
If the problem is that the success class doesn't seem to be working, it's probably that you're missing the table class from your table element (yes, really).
The rule in bootstrap's CSS is:
.table tbody tr.success > td {
background-color: #dff0d8;
}
Note that it starts with a class selector (.table), not a tag selector (table).
So for instance, this markup will not apply those styles to the td in this table:
<table>
<tbody>
<tr id="product1" class = "success">
<td>1</td>
<td>product one</td>
</tr>
</tbody>
</table>
Live Example | Source
But this markup (only change is to the table tag) will:
<table class="table">
<tbody>
<tr id="product1" class = "success">
<td>1</td>
<td>product one</td>
</tr>
</tbody>
</table>
Live Example | Source

jQuery's Order of Insertion for .before() vs. .after()

I'm trying to load in an element created with jQuery to a table. The problem is, this dynamically-created element need to be inserted after another element (the <th>) so that the table retains a nice design. When using .after(), my element is inserted in to the table and the table design looks good, but the data I'm using appears in the opposite order than if I use .before() to insert. Why am I seeing opposite behavior with .before() / .after()?
Edit: .before() gives me the correct order of insertion for the interest rates, but it inserts the elements before the , so the table cols/rows do not line up. .After() gives me the opposite insertion for the interest rates, but the elements are added after that , so the the table retains its rows/cols.
Here's my code as my explanation probably isn't very clear:
<form id="form">
<label for="interestInput" id="interestRateLabel">Enter interest rate</label>
<input id="interestInput" name="interestRate" type="text">
Add another interest rate<br /><br />
<label for="loanAmtInput" id="loanAmtLabel">Enter loan amount</label>
<input id="loanAmtInput" name="loanAmt" type="text">
<button onclick="doCalculation(); return false;">Calculate!</button>
</form>
<div id="standard">
<h1>Standard Repayment</h1>
<table width="600px;" border="1" cellspacing="0" cellpadding="0">
<th scope="col"> </th>
<tr id="totalMonths">
<th scope="row">Total months</th>
</tr>
<tr id="paymentPerMonth">
<th scope="row">Payment/mo</th>
</tr>
<tr id="totalPayment">
<th scope="row">Total payment</th>
</tr>
</table>
</div>
<div id="extended">
<h1>Extended Repayment</h1>
<table width="600px;" border="1" cellspacing="0" cellpadding="0">
<th scope="col"> </th>
<tr id="totalMonths">
<th scope="row">Total months</th>
</tr>
<tr id="paymentPerMonth">
<th scope="row">Payment/mo</th>
</tr>
<tr id="totalPayment">
<th scope="row">Total payment</th>
</tr>
</table>
</div>
<div id="graduated">
<h1>Graduated Repayment</h1>
<table width="600px;" border="1" cellspacing="0" cellpadding="0">
<th scope="col"> </th>
<tr id="totalMonths">
<th scope="row">Total months</th>
</tr>
<tr id="paymentPerMonth">
<th scope="row">Payment/mo</th>
</tr>
<tr id="totalPayment">
<th scope="row">Total payment</th>
</tr>
</table>
</div>
And, here's the relevant JS:
var doCalculation = function() {
$("div#standard table tbody th.rates, div#standard table tbody tr#totalMonths td, div#standard table tbody tr#paymentPerMonth td").remove();
var loanAmount = document.getElementById("loanAmtInput");
$("input[name=interestRate]").each(function(i){
var num = parseInt( $(this).val(), 10 );
var totalMonths = num * 3;
var paymentPerMonth = num + (1/2);
var totalPaymet = num * 120;
console.log(i + "=" + num);
$("div#standard table tbody th[scope=col]").before("<th class=rates>" + num + "% interest rate</th>");
$("div#standard table tbody tr#totalMonths").append("<td>" + totalMonths + "</td>");
$("div#standard table tbody tr#paymentPerMonth").append("<td>" + paymentPerMonth + "</td>");
});
};
It goes in in opposite order because JQuery does each in turn - so in one case, it's running before() on each element, and in the other it's running after(). The way to get the thing you actually want is to start at the <th>, grab next(), and then run before() on that. If you don't have (or might not have) any elements after the <th>, then create a dummy element, insert it after() the <th>, insert the elements you want to insert before() the dummy element, and then delete the dummy element.
If I understand where your confusion is, it's because .before() and .after() work in opposite of each other.
From jQuery API docs:
.before() - Inserts content, specified by the parameter, before each
element in the set of matched elements.
.after() - Inserts content, specified by the parameter, after each
element in the set of matched elements.
Read more at http://api.jquery.com/

IE7 issue with jQuery find?

The following code worked fine in IE7 until I started using IE9.js file. The IE9.js file adds an additional class "ie7_class82" to the already present classes which I added. The code below stopped working in IE7. Is there a known issue with not able to find matching classes with jQuery when multiple classes are present?
--------------HTML code skeleton-------------
<table cellspacing="0" cellpadding="0" bgcolor="#FFF" width="100%">
<thead>
---table rows here----
</thead>
<tbody>
<tr>
<td>
<table cellspacing="0" border="0" width="100%" style="float:left">
<thead>
<tr style="text-align:left;">
<td style="font-size:14px;font-weight:bold;text-align:left;padding:10px 5px">
<span><label class="quarterly">Quarterly</label></span>
<span style="padding:5px">|</span>
<span><label class="monthly">Monthly</label></span>
<span style="padding:5px">|</span>
<span><label class="daily">Daily</label></span>
</td>
</tr>
</thead>
<tbody>
---table rows here----
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table cellspacing="0" border="0" width="100%" style="float:left" class="quarterly">
---table rows here---
</table>
</td>
</tr>
<tr>
<td>
<table cellspacing="0" border="0" width="100%" style="float:left" class="monthly">
---table rows here---
</table>
</td>
</tr>
<td>
<table cellspacing="0" border="0" width="100%" style="float:left" class="daily">
---table rows here---
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
---------jQuery code--------------
$('table thead span label').click(function() {
$(this).parents('table').parents('table').find('table').hide();
$(this).closest('table').find('tbody tr').hide();
$(this).closest('table').show();
$(this).closest('table').find('tbody tr.' + this.className).show();
$(this).parents('table').parents('table').find('table.' + this.className).show();
});​
Note: Unfortunately no errors in IE7(and works fine in FF and Chrome). It is supposed to hide all the tables and show only the ones which match the class name that is present in the label tag. IE7 hides all the tables but fails to show the tables that match the class.
Updated code(that works in IE7, thanks to SO):
$('table thead span label').click(function() {
var classSelector = $.trim($(this).attr('class')).split(/\s+/g).join(',');
$('label:not('+classSelector+')').css('color','#00425f');
$(this).css('color','#d6c9b9');
$(this).parents('table').parents('table').find('table').hide();
$(this).closest('table').find('tbody tr').hide();
$(this).closest('table').show();
$(this).closest('table').find('tbody tr.' + classSelector).show();
$(this).parents('table').parents('table').find('table.'+ classSelector).show();
});
this.className returns the actual class attribute which, in ie7's case, because of the ie9.js file, contains more than one class.
This means that a selector like the one you use :
'table.' + this.className
will be translated into:
'table.yourClassName ie7_class82'
which is not a valid jquery (or css) selector.
I suggest you replace this.className with something like :
var classSelector = $.trim($(this).attr('class')).split(/\s+/g).join('.');
which means that :
'table.' + classSelector
will be translated into :
'table.yourClassName.ie7_class82.some-other-classes-if-any'
Like one comment mentioned, 'tbody tr.' + this.className will generate an invalid selector if this has more than one class.
It's a little confusing why you're trying to get a row that has a class equal to the label you're clicking on. Perhaps take a look at the DOM navigation methods of jQuery. Specifically parent and parents:
http://api.jquery.com/parent/
http://api.jquery.com/parents/
If you absolutely must do what you're trying to do, then the fix would be to replace spaces with periods in this.className. So you could modify your code to do this:
'tbody tr.' + this.className.replace(/ /g,'.')

Categories