Webcomponents based on <table>. Slot out of flow - javascript

I'm try create data-grid with "web component" based on TABLE. I'm use slot in TBODY to append new items. But TR rows rendered out of TBODY. I don't understand this behavior.
shadowDom disallow use TABLE and TR elements, possibly the reason is this, but using HTMLElement.appendChild() also doesn't work
<html>
<head>
<script type="module">
class DataGrid extends HTMLElement {
TEMPLATE_ID = '#data-grid';
constructor() {
super();
this._records = [];
}
connectedCallback() {
let shadow = this.attachShadow({mode: 'open'});
this._render(shadow);
}
_render(shadow){
let tmpl = document.querySelector(this.TEMPLATE_ID);
shadow.appendChild(tmpl.content.cloneNode(true));
//this.appendChild(tmpl.content.cloneNode(true)); //
}
}
customElements.define('data-grid', DataGrid);
</script>
</head>
<body>
<template id="data-grid">
<table border="1">
<thead>
<tr>
<th>id</th>
<th>time</th>
<th>voltage</th>
<tr>
</thead>
<tbody>
<slot></slot>
<tbody>
</table>
</template>
<data-grid>
<!-- need replaced to component
<grid-row time="" voltage="">
-->
<tr>
<td>1</td>
<td>123123123120</td>
<td>12.0</td>
</tr>
<tr>
<td>2</td>
<td>123123133324</td>
<td>12.1</td>
</tr>
<tr>
<td>1</td>
<td>123123122330</td>
<td>12.2</td>
</tr>
</data-grid>
</body>

From the <TR> documentation on MDN:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr
Permitted parents
<table> (only if the table has no child <tbody> element, and even
then only after any <caption>, <colgroup>, and <thead>
elements); otherwise, the parent must be <thead>, <tbody> or
<tfoot>
So
<data-grid>
<tr>
is not valid HTML
move (invalid) lightDOM to a <table> inside <data-grid> shadowDOM
note: attachShadow() both sets and returns this.shadowRoot for free
No need to create your own shadow variable.
psuedo code:
const table = this.shadowRoot.querySelector("TABLE");
this.querySelectorAll("TR").forEach(table.appendChild);

Related

How to modify a HTML <td> (without any unique properties) element using Javascript

I'm trying to modify a element using JS however this element does not have any unique properties like ID. Also the table in which this element resides does not have a unique class. Also, the HTML page has multiple tables and td elements.
For example:
Existing HTML :
<table border="1">
<tbody>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
<tr>
<td>12334567</td>
<td>BirthName</td>
</tr>
</tbody>
</table>
I'm trying to modify the cell which contains the value "BirthName" to "BirthName (Sidharth)"
Something Like this:
<table border="1">
<tbody>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
<tr>
<td>12334567</td>
<td>BirthName (Sidharth)</td>
</tr>
</tbody>
</table>
You can find all having BirthName by using bellow colde
const allTds = document.querySelectorAll('td')
// Find the td element that contains the text "BirthName"
const birthDateTd = Array.from(allTds).filter(td=>td.textContent==='BirthName')
After that you can target that <td> as you want.
You can do checking the text for all td and change where matches birthname
let element = document.querySelectorAll('td');
for(let i = 0; i<element.length; i++){
if(element[i].innerText == 'BirthName'){
element[i].innerText += '(Sidharth)';
}
}
If the text is unique then you can use Xpath as shown below and change it.
var td = document.evaluate("//td[contains(text(), 'BirthName')]", document, null, XPathResult.ANY_TYPE, null );
var thisTd = td.iterateNext();
thisTd.innerHTML = "BirthName (Sidharth)";
<table border="1">
<tbody>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
<tr>
<td>12334567</td>
<td>BirthName</td>
</tr>
</tbody>
</table>

Creating a custom table row

I am trying to create a custom table row but having difficulty getting it to behave properly. I've tried the two below methods and they give bizarre results. I realize that this is very easy to to without custom elements but this is a small example of a much larger project. What can I change to achieve the desired result?
class customTableRow extends HTMLElement {
constructor(){
super();
var shadow = this.attachShadow({mode: 'open'});
this.tableRow = document.createElement('tr');
var td = document.createElement('td');
td.innerText = "RowTitle";
this.tableRow.appendChild(td);
var td2 = document.createElement('td');
td2.innerText = "RowContent";
td2.colSpan = 4;
this.tableRow.appendChild(td2);
shadow.appendChild(this.tableRow);
}
}
customElements.define('custom-tr', customTableRow);
//Attempt 2
var newTr = new customTableRow;
document.getElementById('table2Body').appendChild(newTr);
td {
border: 1px solid black;
}
<span>Attempt 1:</span>
<table>
<thead>
<tr>
<th>One</th>
<th>Two</th>
<th>Three</th>
<th>Four</th>
<th>Five</th>
</tr>
</thead>
<tbody>
<custom-tr />
</tbody>
</table>
<hr>
<span>Attempt 2:</span>
<table id="table2">
<thead>
<tr>
<th>One</th>
<th>Two</th>
<th>Three</th>
<th>Four</th>
<th>Five</th>
</tr>
</thead>
<tbody id="table2Body">
<!-- It should append here -->
</tbody>
</table>
<hr>
<span>This is how I want it to look:</span>
<table id="table2">
<thead>
<tr>
<th>One</th>
<th>Two</th>
<th>Three</th>
<th>Four</th>
<th>Five</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row Title</td>
<td colspan="4">Row Content</td>
</tbody>
</table>
A <table> element and its subcomponents <tbody>, <tr> require a very specific syntax. For example, only <tr> elements are authorized as children of <tbody>.
Therefore you cannot define a element and insert it in <tbody> or <table>. If you do that it will be moved outside of the <table> at parsing. Hence the display of your first example (look the code in the Dev Tools).
Instead you should define a customized tag instead like in this answer to a similar question.
Or you should redefine a complete custom table structure with <custom-table>, <custom-tbody>... like in this other answer.
Also, you should use closing tag <custom-tr></custom-tr>, and insert your CSS rule in the Shadow DOM if you want it to by applied inside it.

How to create table with multiline cells in React-bootstrap?

I want to create table where some cells contain several lines.
It's work if I do it:
<Table bordered>
<thead>
<tr>
<th>Date</th>
<th>Analysed ID</th>
<th>Analysed Name</th>
<th>Solve To change</th>
</tr>
</thead>
<tbody>
<tr>
<td rowSpan="3">Date</td>
</tr>
<tr>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
<tr>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
</tbody>
</Table>
I got it:
Table with multiline cell
And now I want to add my 3 "TR" tags in one component, because after I want use for-cycle to create many such components. But components must return content in one closed tag. I tried to contain my 3 "tr" in one parent "tr", but I got error. What can I do here?
It is not possible to create a React Component that returns three elements without wrapping them in another element, such as a div. Otherwise, you'll get the following error:
A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
Your case here is a bit special, because you cannot have div's as the immediate child of table or tbody, so that's a problem...
What you can do however, is to create a class function that returns an array. Like this:
class MyApp extends React.Component {
getTr = () => {
return [
<tr key={0}>
<td rowSpan="3">Date</td>
</tr>,
<tr key={1}>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>,
<tr key={2}>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
];
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>Date</th>
<th>Analysed ID</th>
<th>Analysed Name</th>
<th>Solve To change</th>
</tr>
</thead>
<tbody>
{this.getTr()}
{this.getTr()}
{this.getTr()}
</tbody>
</table>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You need to include tr tags in one div tag.
The right way to rowSpan is this:
var MyRow = React.createClass({
render: function () {
return (
<div>
<tr>
<td rowSpan="2">{this.props.item.date}</td>
<td>{this.props.item.data[0].id}</td>
<td>{this.props.item.data[0].name}</td>
<td>{this.props.item.data[0].solve}</td>
</tr>
<tr>
<td>{this.props.item.data[1].id}</td>
<td>{this.props.item.data[1].name}</td>
<td>{this.props.item.data[1].solve}</td>
</tr>
</div>
);
}
});
This is my working example: http://jsfiddle.net/andrea689/e33pd14L/

Get the text inside TD by neighbouring TH inner text

i need to get the value of a specific TD which has no ID or Class by the text value of a neighbouring TH using jQuery
Example:
<table>
<tbody>
<tr>
<th>Established</th>
<td>1976</td>
</tr>
<tr>
<th>Location</th>
<td>Baltimore, Maryland</td>
</tr>
</tbody>
</table>
I want to get the year 1976 (using jQuery) by searching for "Established"
the location of the tr / order isnt always the same.
Possible?
var year = $("th:contains('Established')").next().text();
console.log(year); // "1976"
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th>Established</th>
<td>1976</td>
</tr>
<tr>
<th>Location</th>
<td>Baltimore, Maryland</td>
</tr>
</tbody>
</table>
the above code will work given the string Established will always be in that format (First-uppercase, no spaces, etc).
A more robust solution:
var year = $("th").filter(function(){
return /^established$/i.test($.trim($(this).text()));
}).nextAll().filter(function() { // If there's more sibling TD...
// get all that have exactly 4 numbers as text
return /^\d{4}$/.test($.trim($(this).text()));
}).first().text(); // ...but get the first one's text.
console.log(year); // "1976"
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th> EstablishEd </th> <!-- Contains spaces and possible uppercase errors? -->
<td>1976</td>
</tr>
<tr>
<th>Location</th>
<td>Baltimore, Maryland</td>
</tr>
<tr>
<th>Workers</th>
<td>3100</td>
</tr>
</tbody>
</table>
https://api.jquery.com/contains-selector/
https://api.jquery.com/next/
https://api.jquery.com/text/
Yes, it is possible. jQuery's .filter(callback) method can be used to filter elements based on their content.
Select th elements
Filter selected th elements to only have "Established" ones
Select the td elements that follow these th elements
var years = $("th").filter(function() { //Get th elements that has "Established" as their text
return $(this).text() === "Established";
}).next("td"); //Get td elements that are next to these th elements;
console.log(years.text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th>Established</th>
<td>1976</td>
</tr>
<tr>
<th>Location</th>
<td>Baltimore, Maryland</td>
</tr>
</tbody>
</table>

jquery how to get the class of parent element using chlid id?

I'm trying to alert the table(parent) class on click using the child id <th id="first">.
alert($(this).parent('tr').attr('class')); using this i have got the class of the <tr>.
But i want to get the class of the table when i use alert($(this).parent('table').attr('class')); it showing alert says undefined can someone help me how can the parent class using the child id
<table class="table table-bordered">
<thead>
<tr class="sample">
<th id="first">Firstname</th>
<th id="last">Lastname</th>
<th id="user">Username</th>
</tr>
</thead>
<tbody>
<tr>
<td>arun </td>
<td>kumaresh</td>
<td>arun kumaresh</td>
</tr>
</tbody>
</table>
</div>
<script>
$(document).ready(function(){
$("#first").click(function(){
alert($(this).parent('tr').attr('class'));
});
});
</script>
Use .closest(TARGET_SELECTOR), For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
As .parent(SELECTOR), Get the parent of each element in the current set of matched elements, optionally filtered by a selector. (the parent() method traverses to the immediate parent)
$(document).ready(function() {
$("#first").click(function() {
alert($(this).closest('table').attr('class'));
});
$("#last").click(function() {
alert($(this).closest('thead').attr('class'));
});
$(".user").click(function() {
alert($(this).closest('table').attr('class'));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<table class="table table-bordered">
<thead class='ANY_CLASS'>
<tr class="sample">
<th id="first">Firstname</th>
<th id="last">Lastname</th>
<th class="user">Username</th>
</tr>
</thead>
<tbody>
<tr>
<td>arun</td>
<td>kumaresh</td>
<td>arun kumaresh</td>
</tr>
</tbody>
</table>
simply use
alert($(this).closest('table').attr('class'));
refer closest here
https://api.jquery.com/closest/

Categories