insertAdjacentHTML not working with created element - javascript

I want to display a table after an h1 with an id of pageHeading.
The HTML for the h1 is hardcoded: <h1 id="pageHeading">Table</h1>
const pageHeading = document.querySelector("#pageHeading")
The table is created with Javascript:
const table = document.createElement("table")
table.setAttribute("id", "table")
I tried the following:
document.body.appendChild(table)
This prints the table but after the last HTML element on the page.
Then I tried:
tableHeading.appendChild(table)
This prints the table but INSIDE the h1.
Finally, I tried:
pageHeading.insertAdjacentHTML(
"afterend",
table
)
This doesn't print the table at all. Instead I get (after the h1):
[object HTMLTableElement]
Could this be because I'm using .insertAdjacentHTML on the table contents (see full code below)?
const tableHeaders = [{
titleOne: "Name",
titleTwo: "Age",
titleThree: "Nationality",
}, ]
const persons = [{
name: "James",
age: "23",
nationality: "English",
},
{
name: "Isabella",
age: "21",
nationality: "Italian",
},
{
name: "Hank",
age: "25",
nationality: "American",
},
{
name: "Manon",
age: "27",
nationality: "French",
},
]
const pageHeading = document.querySelector("#pageHeading")
const table = document.createElement("table")
table.setAttribute("id", "table")
/* document.body.appendChild(table) this puts table AFTER the last item in the body <h2>Test</h2> */
/* tableHeading.appendChild(table) this puts table INSIDE <h1 id="tableHeading">Table</h1> */
pageHeading.insertAdjacentHTML(
"afterend",
table
) /* this returns: [object HTMLTableElement] */
const headers = tableHeaders.map(header => {
let ths = `<tr><th>${header.titleOne}</th><th>${header.titleTwo}</th><th>${header.titleThree}</th></tr>`
table.insertAdjacentHTML("afterbegin", ths)
})
const personDetails = persons.map(person => {
let tds = `<tr><td>${person.name}</td><td>${person.age}</td><td>${person.nationality}</td></tr>`
table.insertAdjacentHTML("beforeEnd", tds)
})
<h1 id="pageHeading">Table</h1>
<h2>Test</h2>

Instead of use insertAdjacentHTML you need insertAdjacentElement because is a element not an html string like:
const tableHeaders = [{
titleOne: "Name",
titleTwo: "Age",
titleThree: "Nationality",
}, ]
const persons = [{
name: "James",
age: "23",
nationality: "English",
},
{
name: "Isabella",
age: "21",
nationality: "Italian",
},
{
name: "Hank",
age: "25",
nationality: "American",
},
{
name: "Manon",
age: "27",
nationality: "French",
},
]
const pageHeading = document.querySelector("#pageHeading")
const table = document.createElement("table")
table.setAttribute("id", "table")
/* document.body.appendChild(table) this puts table AFTER the last item in the body <h2>Test</h2> */
/* tableHeading.appendChild(table) this puts table INSIDE <h1 id="tableHeading">Table</h1> */
pageHeading.insertAdjacentElement(
"afterend",
table
) /* this returns: [object HTMLTableElement] */
const headers = tableHeaders.map(header => {
let ths = `<tr><th>${header.titleOne}</th><th>${header.titleTwo}</th><th>${header.titleThree}</th></tr>`
table.insertAdjacentHTML("afterbegin", ths)
})
const personDetails = persons.map(person => {
let tds = `<tr><td>${person.name}</td><td>${person.age}</td><td>${person.nationality}</td></tr>`
table.insertAdjacentHTML("beforeEnd", tds)
})
<h1 id="pageHeading">Table</h1>
<h2>Test</h2>
Reference:
insertAdjacentHTML
insertAdjacentElement

Related

jspdf autotable make header as first column instead of top row

I'm making use of JSDF autotable but when i generate the PDf file then header is coming as top row but I want the header as the first column instead of top row, Please help
My code below:
const { jsPDF } = require("jspdf");
require('jspdf-autotable');
var result = [
{
name: "Gautam Sharma",
age: 32,
country: "India",
},
{
name: "John Williamson Latham",
age: 31,
country: "New Zealand",
},
{
name: "Adam Nicholls",
age: 31,
country: "South Africa",
},
];
let info = []
result.forEach((element, index, array) => {
info.push([element.name, element.age, element.country])
});
var doc = new jsPDF();
doc.autoTable({
head: [["Name", "Age", "Country"]],
body: info
});
doc.save("table.pdf");
Output pdf file as below:
Expected PDF file I want header in first column:
Please help

Copying data from a kendo treelist with keeping the table structure

I have an editable multi selectable kendo Treelist. I would like to be able to select part of the grid and copy paste its data in the same grid (other columns and rows) or to a text file. It is important to paste it with the same structure in the new table.
The copy feature is not supported for kendo Treelist.
Is there a way to do that with use of JavaScript and jQuery?
Kendo demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2023.1.117/styles/kendo.default-v2.min.css"/>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2023.1.117/js/kendo.all.min.js"></script>
</head>
<body>
<div id="treeList"></div>
<script>
$("#treeList").kendoTreeList({
columns: [
{ field: "name" },
{ field: "age" }
],
selectable: "multiple, cell",
editable:"incell",
dataSource: [
{ id: 1, parentId: null, name: "Jane Doe", age: 22 },
{ id: 2, parentId: 1, name: "John Doe", age: 24 },
{ id: 3, parentId: 1, name: "Jenny Doe", age: 3 }
]
});
</script>
</body>
</html>
I have used two buttons, one for copying and one for pasting. The events functions are as below. This solved my problem and I can also paste the copied text in excel.
<button onClick="copying()" >Copy</button>
<button onClick="pasting()" >Paste</button>
<div id="treeList"></div>
<script>
$("#treeList").kendoTreeList({
columns: [
{ field: "name" },
{ field: "age" }
],
selectable: "multiple, cell",
editable:"incell",
dataSource: [
{ id: 1, parentId: null, name: "Jane Doe", age: 22 },
{ id: 2, parentId: 1, name: "John Doe", age: 24 },
{ id: 3, parentId: 1, name: "Jenny Doe", age: 3 }
]
});
</script>
var copiedText="";
function copying(){
if(copiedText !== ""){
return;
}
var grid = $("#treeList").data("kendoTreeList");
var selected = grid.select();
var previousRowID = selected.eq(0).parent().index();
var isNewLine = true;
selected.each(function() {
var row = $(this).closest("tr");
var dataItem = grid.dataItem(this);
if (previousRowID !== $(this).parent().index()) {
copiedText += "\r\n";
isNewLine = true;
}
previousRowID = $(this).parent().index();
var colIndx = $("td", row).index(this);
var column = grid.columns[colIndx];
var data = dataItem;
var value = dataItem[column.field];
if (!isNewLine) {
copiedText += "\t";
}
copiedText += value;
isNewLine = false;
});
var textarea = $("<textarea>");
var offset = $(this).offset();
// Position the textarea on top of the Treelist and make it transparent.
textarea.css({
position: 'absolute',
opacity:0,
border: 'none',
width: $(this).find("table").width(),
height: $(this).find(".k-grid-content").height()
});
textarea.val(copiedText)
.appendTo('body')
.focus()
.select();
document.execCommand('copy');
setTimeout(function(){
textarea.remove();
});
}
function pasting() {
var pasteVal = copiedText;
var grid = $("#treeList").data("kendoTreeList");
if (pasteVal) {
var selectedArr= Object.values($(".k-grid td.k-selected"));
var pasteArray = pasteVal.split("\r\n").filter(r => r !== "").map(r => r.split("\t"));
pasteArray.forEach(function( item, index) {
selectedArr[index].innerHTML = item;
});
grid.refresh();
}
copiedText= "";
}

Accessing nested json in ExpandRow of bootstrap table react component

I have a bootsrap table react component and I will like to add the nested json record as a table when each row in the bootstrap table, the row should expand and show the nested json has a table. However, I can not access the nested json in the expand row function.
My Goal: Presently I have a table showing the data of the outer json in a table. However, I want the elements in the the nested json summary to be represented in a table when each row is clicked.
const userList = [
{ id: 1, firstName: "james", lastName: "smith", age: 20,summary:{city:'lag', min:12} },
{ id: 2, firstName: "alex", lastName: "green", age: 20 , summary:{city:'lao', min:121}},
{ id: 3, firstName: "may", lastName: "jones", age: 18, summary:{city:'dag', min:112} }
];
columns=[
{text:'id',dataField:id},
{text:'firstName',dataField:id firstName},
{text:'lastName',dataField: lastName}
]
const expandRow = {
renderer: row => (
<BoostrapTable data={userList} columns={row.columns.summary}/>
)
};
<BoostrapTable data={userList} columns={columns} keyField="id expandRow={ExpandRow}/>
You are using the wrong variable, it's not rows, but row:
const expandRow = {
renderer: row => (
<BoostrapTable data={[row.summary]} columns={{text:'city',dataField:'city'},
{text:'min',dataField:'min'}}/>
)
};

Loop through an array of objects and display in HTML

I have an array as below:
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
Now, i want to loop through the array and display them int the below dom:
<div id="locations-grid">
<div id="di-locations">
<div id="plot"></div>
<div id="address"></div>
<div id="city"></div>
<div id="pin"></div>
<div id="phone"></div>
</div>
</div>
Each object in the array corresponds to one location. I want to display all the locations as different columns in a css grid.
I tried
locations.map((item)=>{
plot.innerHTML = item.plot;
address.innerHTML = item.address;
city.innerHTML = item.city;
pin.innerHTML = item.pin;
phone.innerHTML = item.phone;
});
But this displays only the last object in the array. This doesn't get me the 3 objects into 3 different columns of the grid.
Putting your structure in DIVS is not the right way use a TABLE instead of it (here with a header-row). You had to dynamical build the rows and cell of it because you don't know how many rows will be needed and it's much more structured.
First get the handler from your table with document.getElementById Than create for every row a new table-row TR and then iterate through your data. For each property of it create a new cell TD and add to it's innerHTML the value. Append the TD to your row TR with appendChild because the elemnts is till now not in the DOM. After you have done this for every property append the TR to the table. Now the tablerow will be presented in the table.
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}
];
let table = document.getElementById('di-locations');
locations.forEach(location => {
let tr = document.createElement('tr');
Object.entries(location).forEach(value => {
let td = document.createElement('td');
td.innerHTML= value;
tr.appendChild(td);
});
table.appendChild(tr);
});
td, th { border: 1px solid black; }
<div id="locations-grid">
<table id="di-locations">
<tr>
<th>plot</th>
<th>address</th>
<th>city</th>
<th>pin</th>
<th>phone</th>
</tr>
</table>
</div>
You can iterate over these location objects and append new grid items containing the location property data as you go. From your question it's a little unclear how exactly the result should look like, but this snippet gives you something to build upon...
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
var container = document.getElementById("di-locations");
// iterate locations
for (loc of locations) {
// iterate location properties
for (var prop in loc) {
if (Object.prototype.hasOwnProperty.call(loc, prop)) {
//create and append grid item
var item = document.createElement("DIV");
item.classList.add(loc[prop]);
item.innerHTML = loc[prop];
container.appendChild(item);
}
}
}
#di-locations {
display: grid;
grid-template-columns: auto auto auto auto auto;
font-family: sans-serif;
}
#di-locations>* {
padding: .8rem;
border-bottom: 1px solid #ddd;
}
<div id="locations-grid">
<div id="di-locations"></div>
</div>
This should help, shouldn't be too hard to turn into a grid:
HTML:
<div id="i"></div>
JavaScript:
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
txt = "<p>"; // Define txt so it can be accessed inside of functions
locations.forEach(foo); // Run foo for each item in the array
function foo(value,index,array) {
txt = txt + "Plot: " + value["plot"] + "<br>"; // Add Plot: _____ to the end of txt
txt = txt + "Address: " + value["address"] + "<br>"; // Similar to above
txt = txt + "City: " + value["city"] + "<br><br>"; // Similar to above
}
document.getElementById("i").innerHTML = txt + "</p>"; // Set the inner html of i to txt

How to get a certain data in the second autocomplete input that depend on what typed in the first input in React.js?

Okay, so I don't know how to properly express my simple problem because of how simple it is, I guess.
Basically, I have an autocomplete done by me in my React project.. I have two inputs "Country" and "City". When I type a country my autocomplete works great giving me suggestions but now I have to make the same for my second input so it would give me a list of cities that depends on which country is typed in the "Country" input...
"United Kingdom" => "London, Birmingham, Bighton etc."
How can I do that? Thank you!
P.S. I already have all the lists of countries and cities, I just don't know how to make the second input to depend on an information in the first one.
Code here
Autocomplete.jsx
https://github.com/lembas-cracker/Weather-app/blob/master/src/Autocomplete.jsx
Form.jsx
https://github.com/lembas-cracker/Weather-app/blob/master/src/Form.jsx
P.S. I already have all the lists of countries and cities, I just don't know how to make the second input to depend on an information in the first one.
If you know which country the city belongs to (perhaps via a key in the city object), you could run a simple filter function to remove any cities that don't belong to that country.
this.state = {
selectedCountry: 'London',
};
const cities = [
{ name: "Toronto", country: "Canada" },
{ name: "London", country: "United Kingdom" }
];
const filteredCities = cities.filter(city => {
return city.country !== this.state.selectedCountry;
});
On your city input field make sure to create an onBlur function to will run the filter on your cities list once the user leaves that input field.
Made a quick example. Did you mean smth like this? Since you haven't provided any part of your source code, I used plain HTML select for the demo.
https://jsfiddle.net/arfeo/n5u2wwjg/204186/
class App extends React.Component {
constructor() {
super();
this.state = {
countryId: 1,
};
}
onCountryChange(countryId) {
this.setState({ countryId: parseInt(countryId) });
}
render() {
return (
<div>
<Input
key="countriesInput"
type="countries"
countryId={this.state.countryId}
onChange={(countryId) => this.onCountryChange(countryId)}
/>
<Input
key="citiesInput"
type="cities"
countryId={this.state.countryId}
/>
</div>
);
}
}
class Input extends React.Component {
constructor() {
super();
this.selectRef = null;
}
renderOptions() {
const countries = [
{
id: 1,
name: 'England',
},
{
id: 2,
name: 'Germany',
},
{
id: 3,
name: 'France',
},
];
const cities = [
{
countryId: 1,
cities: [
{
id: 1,
name: 'London',
},
{
id: 2,
name: 'Liverpool',
},
{
id: 3,
name: 'Salisbury'
}
],
},
{
countryId: 2,
cities: [
{
id: 4,
name: 'Berlin',
},
{
id: 5,
name: 'Frankfurt',
},
],
},
{
countryId: 3,
cities: [
{
id: 6,
name: 'Paris',
},
],
},
];
switch (this.props.type) {
case 'countries': {
return countries.map((country) => (
<option
key={country.id.toString()}
value={country.id}
>
{country.name}
</option>
));
}
case 'cities': {
const citiesMap = cities.filter((city) => city.countryId === this.props.countryId);
if (citiesMap && citiesMap[0]) {
const citiesList = citiesMap[0].cities;
if (citiesList) {
return citiesList.map((city) => (
<option
key={city.id.toString()}
value={city.id}
>
{city.name}
</option>
));
}
}
return null;
}
default: return null;
}
}
render() {
return (
<select name={this.props.type} ref={(ref) => this.selectRef = ref} onChange={() => this.props.onChange(this.selectRef.value)}>
{this.renderOptions()}
</select>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
UPDATE
Make your Form component stateful.
Add a state property for countries in Form (let it be countryId).
Pass this property as a prop into the second Autocomplete component.
When the first Autocomplete changes, change the countryId of the Form.
I've done something similar which may help you.
The Object.keys(instutiontypes) you could use to have an array of countries, instead. Then inside of those values, you can have an array of objects. You could have the cities here, e.g. {value: "Manchester", "label: Manchester", phoneExt: "0114"}
const instutiontypes = {
Kindergarten: [
{ value: "PreK", label: "PreK" },
{ value: "K1", label: "K1" },
{ value: "K2", label: "K2" },
{ value: "K3", label: "K3" },
],
"Primary School": [
{ value: "Grade 1", label: "Grade 1" },
{ value: "Grade 2", label: "Grade 2" },
{ value: "Grade 3", label: "Grade 3" },
{ value: "Grade 4", label: "Grade 4" },
{ value: "Grade 5", label: "Grade 5" },
{ value: "Grade 6", label: "Grade 6" },
],
}
To have the options in my input, I use Object.keys(instutiontypes) to get ['Kindergarten','Primary School']
Then, to get the array of ages to give to my secondary dropdown, I have written this code:
const types = ['Selection1', 'Selection2']
const agesList = [];
for (let i = 0; i < types.length; i++) {
Object.values(institutionTypes[types[i]]).map(({ label }) =>
agesList.push(label)
);
}
This way, the ages dropdown list is dependent on the values passed to institutionTypes.
I'm using mui's <Autocomplete /> components to make them be search dropdowns, with the prop options for the arrays.

Categories