Accessing table cells in jQuery - javascript

How come this works:
alert(document.getElementById("tableId").rows[7].cells[7].innerHTML);
But not this:
alert($("#tableId").rows[7].cells[7].innerHTML);
For context, I had used .append to add in cells to the table, and I want to be able to manipulate each cell further in the future (such as using .data, which is why I want to use jQuery.
Alternatively, are there other ways to access the individual cells in the table? Thank you.

The former works because getElementById() returns an HTMLTableElement object which has the rows property. The latter uses a jQuery object which does not have the rows property, so you get an error.
You could solve this by retrieving the underlying Element from the jQuery object in one of two ways:
console.log($("#tableId")[0].rows[7].cells[7].innerHTML); // access by index
console.log($("#tableId").get(0).rows[7].cells[7].innerHTML); // get(0)
Alternatively you could use jQuery selectors to select the cell you want directly:
let html = $('#tableId tr:eq(7) td:eq(7)').html();
For context, I had used .append to add in cells to the table, and I want to be able to manipulate each cell further in the future
If this is the case you can save a reference to the content you add so that you can use it again without having to perform a DOM access operation. In pseudo-code it would look something like this:
let $content = $('<tr><td>Foo</td></tr').appendTo('#tableId');
// in some other function later in the page execution...
$content.find('td').text('bar').data('lorem', 'ipsum');

If you are looking to loop over each row
$('#tableId tr').each(function() {
if (!this.rowIndex) return; // to skip first row if you have heading
console.log(this.cells[0].innerHTML);
});

Related

Use parentElement multiple times

I'm editing a plugin that creates filters on an table like excel(drop-down), the problem is when I use it on a table that uses a table inside it, in fact the plugin will also take the values ​​of the sub-table.
I therefore decided to exclude from the initial array, made up of all the rows, those elements that have a parent with a table that does not have an id.
So i forEach array and see if have id like:
this.tds.forEach((el) =>{
console.log(el.parentElement.parentElement.parentElement.id);
});
I was wondering if using parentElement three times like this is correct or there is another way
It's perfectly fine, as long as you're sure that the structure will always be the same.
But let's assume that you don't know if the structure will always be like this, but you do know the class of the parent you're looking for (or any other CSS query), then you could use the Element.closest() method to query your way up.
So let's say you want to find the closest table with an id value.
this.tds.forEach((el) => {
const parent = el.closest('table:not([id=""])');
if (parent !== null) {
console.log(parent.id);
}
});
This will walk up the DOM tree from the el as starting point, doing something in the likes of parentElement.parentElement.parentElement... until it reaches an element that has a value in the id attribute.
There's nothing wrong with the code.
But if you want to make the code a little more robust you can use Optional Chaining.
this.tds.forEach((el) =>{
console.log(el?.parentElement?.parentElement?.parentElement?.id);
});

Does jquery `append()` remove duplicate objects?

I'm sorting a table using jQuery, roughly following the code found here. The code sketch is as follows:
$('.sort-table').click(function(e) {
// cache objects
$table = $('#sort-table'), // cache the target table DOM element
$rows = $('tbody > tr', $table); // cache rows from target table body
// sort items
$rows.sort(<my_predicate_function(a,b){...}>);
// assign to table - what is going on?
$rows.each(function(index, row){
$table.append(row); // <-- how come $table remains the same size?
});
});
While the code works fine, I'm puzzeled by the code that appends the rows back to the table, which simply iterates over the sorted rows, appending each at the end of $table.
At no stage did we emtpy $table from its previous children.
Since $table was never emptied, how come $table remains the same size?
Does append() also enforces uniquness in the target container?
This is simply how the DOM works. You can't have an element in two different places. If an element is already in a document and you put it somewhere else, it will be moved from its current position. It is, I suppose, a little like a Set, but that is not how it is specified. So it's not removing duplicate objects, because there never are duplicate objects: it's simply moving the same object, which can only exist in one place.
From the MDN documentation for the underlying method, Node.appendChild:
The Node.appendChild() method adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position (there is no requirement to remove the node from its parent node before appending it to some other node).
If you want to duplicate elements, you need to clone them (DOM, jQuery).
As from JQuery doc for .append()
Description: Insert content, specified by the parameter, to the end of each element in the set of matched elements.
As from MDM doc for Set
The Set object lets you store unique values of any type, whether primitive values or object references.
Nothing gets removed.
You only will replace the existing element if it's exactly the same, which is the case in your example.

Get Row Number with Handsontable

Super noob question here. I have an array of row indexes that I would like to use to change the color of my Handsontable rows. I figure HOT would provide a method to retrieve the tr element of a table with something like hot.getRow(5), but it doesn't seem to exist.
So in a nutshell I'm trying to do this
var rowIds = []
$.each(rowIds , function (i, element) {
var row = hot.getRow(i);
$(row).closest('tr').css('color','green');
});
I've found I can use getCell() method which accepts a row and column # along with a boolean, but using this would require extra code for something that should be as simple as passing a single argument. Is there a method I'm overlooking or is this the only way?
Here's the thing with how HOT works: it is a JS object which renders a stateless DOM table. This means you should never EVER try to manually modify the HTML of your table. Even if you did want to do that, as soon as you make a change to those green cells, they would get re-rendered, not green.
Instead, you want to use the readily accessible 'custom renderers' that are associated with each column or cell, depending on how you define them. These are applied just like the data attribute in the columns or cells definition. They are functions and here's an example:
function greenCellRenderer(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.color = 'green';
}
You should read up on the full documentation to understand the full power of the renderer but it's pretty clear from the arguments it takes what you can do. One thing you would be able to do is apply the green color you're hoping for. Of course to selectively do this you would either apply the renderer to specific cells, or put a conditional inside this general renderer using the row and col arguments to your advantage.
Hope that helps!

Looping through selected checkboxes jQuery

I'm trying to get the values of all selected checkboxes with the following code to insert them in a textarea.
$('input[name="user"]:checked').each(function(){
parent.setSelectedGroup($(this).val()+ "\n");
});
but i always get only one value.
How to write the code in a correct way to get the value of ALL selected checkboxes?
Thanks ahead!
EDIT
1) "parent" because the checkboxes are in a fancybox.iframe.
2) setSelectedGroup in the parent window is
function setSelectedGroup(groupText){
$('#users').val(groupText);
You are getting all the values, simply on each loop through the collection you're passing a new value to setSelectedGroup. I assume that method replaces content rather than appending so you are simply not seeing it happen because its too fast.
parent.setSelectedGroup(
//select elements as a jquery matching set
$('[name="user"]:checked')
//get the value of each one and return as an array wrapped in jquery
//the signature of `.map` is callback( (index in the matching set), item)
.map(function(idx, el){ return $(el).val() })
//We're done with jquery, we just want a simple array so remove the jquery wrapper
.toArray()
//so that we can join all the elements in the array around a new line
.join('\n')
);
should do it.
A few other notes:
There's no reason to specify an input selector and a name attribute, usually name attributes are only used with the input/select/textarea series of elements.
I would also avoid writing to the DOM inside of a loop. Besides it being better technique to modify state fewer times, it tends to be worse for performance as the browser will have to do layout calculations on each pass through the loop.
I strongly recommend almost always selecting the parent element for the parts of the page that you're concerned with. And passing it through as the context parameter for jquery selectors. This will help you scope your html changes and not accidentally modify things in other parts of the page.

Getting all HTML elements in the DOM where an attribute name starts with some-string

I've stumbled upon a tricky one, that I haven't been able to find any references to (except one here on Stackoverflow, that was written quite inefficiently in Plain Old Javascript - where I would like it written in jQuery).
Problem
I need to retrieve all child-elements where the attribute-name (note: not the attribute-value) starts with a given string.
So if I have:
<a data-prefix-age="22">22</a>
<a data-prefix-weight="82">82</a>
meh
My query would return a collection of two elements, which would be the first two with the data-prefix--prefix
Any ideas on how to write up this query?
I was going for something like:
$(document).find("[data-prefix-*]")
But of course that is not valid
Hopefully one of you has a more keen eye on how to resolve this.
Solution
(See accepted code example below)
There is apparently no direct way to query on partial attribute names. What you should do instead (this is just one possible solution) is
select the smallest possible collection of elements you can
iterate over them
and then for each element iterate over the attributes of the element
When you find a hit, add it to a collection
then leave the loop and move on to the next element to be checked.
You should end up with an array containing the elements you need.
Hope it helps :)
Perhaps this will do the trick -
// retrieve all elements within the `#form_id` container
var $formElements = $("form#form_id > *");
var selectedElements = [];
// iterate over each element
$formElements.each(function(index,elem){
// store the JavaScript "attributes" property
var elementAttr = $(this)[0].attributes;
// iterate over each attribute
$(elementAttr).each(function(attIndex,attr){
// check the "nodeName" for the data prefix
if (attr.nodeName.search('data-.*') !== -1){
// we have found a matching element!
if (selectedElements.length < 2){
selectedElements.push(elem);
break;
}else{
if (selectedElements.length == 2){
break(2);
}
}
}
});
});
selectedElements will now hold the first two matching elements.
jsFiddle
You can use jquerys filter() method to have a selections elements being processed by a function. Inside that function you are free to do whatever you want to.
So start with selecting all elements inside the dom tree and filter out everything you dislike. Not especially fast, but working.

Categories