Ok let's look at this part of the code -
$(tbody).find('tr').each((i, oldTbodyTr) => {
newTr = document.createElement('tr');
$(oldTbodyTr).find('td').each((i, oldTd) => {
let newTd = document.createElement('td');
newTd.innerHTML = oldTd.innerHTML;
newTd.classList = oldTd.classList;
newTd.style = oldTd.style; //Doesn't work
newTr.appendChild(newTd);
});
newTableTbody.appendChild(newTr);
});
It creates a new table body by looping through all rows in tbody of an already existing table.
Everything is fine, except that the style of the old td doesn't transfer to the new td element.
I can't figure out why.
If you want to copy inline styles, you could try newTd.style.cssText = oldTd.style.cssText since style itself is immutable.
See more about style.cssText.
const source = document.getElementById('source');
const target = document.getElementById('target');
target.style.cssText = source.style.cssText;
<p id="source" style="color: red; background-color: blue; font-size: 20px;">Source</p>
<p id="target">Target</p>
Related
I've got an inventory for items in my javascript game.
I'm trying to make it so that when you mouseover an inventory item it shows a text overlay description of the item.
My idea was to append a div with the mouseover "reveal()" function once the item is added to the inventory but for some reason its not working. It works with the item text being ::before or ::after the div but i cant seem to get a text overlay of the item image even when playing around with the Z-Index.etc
I've tried a simple :hover in CSS but couldnt get it to work that way either.
I can "spawn" the item with the text on top from the start i just cant seem to get it so that the text only appears on a mouse over.
Hopefully i have explained it in a way that makes sense:
const textElement = document.getElementById('text');
const imgElement = document.getElementById('room-image');
const optionButtonsElement = document.getElementById('option-buttons');
const button3 = document.getElementById('TEST');
const inventory = document.getElementById('inventory');
const itemContainer = document.getElementById('imageContainer');
const itemText = document.getElementById('itemtext');
let textArrayIteration = 1
function reveal() {
itemText.classList.toggle('on');
}
//ADDING OBJECT VALUE TO INVENTORY///
function pullValue() {
var node = document.createElement("P");
var textNodeItems = Object.values(items);
var textNode = (document.createTextNode(textNodeItems));
node.appendChild(textNode);
document.getElementById("inventory").appendChild(node);
//ASSIGNING THE IMAGE AND DIVS FOR THE ITEM//
if (inventory.innerHTML.indexOf("test") !== -1) {
var a = document.createElement("div")
a.setAttribute("id", "Div1");
var iconUrl = document.createElement("img");
iconUrl.src = "test.jpg";
a.appendChild(itemText);
a.appendChild(iconUrl);
inventory.appendChild(a);
node.style.display = "none";
}
.itemtext-on {
position: absolute;
display: none;
z-index: 200;
}
.itemtext {
position: absolute;
display: block;
font-size: 80px;
top: 50px;
color: red;
z-index: 200;
}
<div id="itemtext" class="itemtext" onmouseover="reveal()">test</div>
You have not defined the class on.
I see that you have defined .itemtext-on.
If this is a typo, then change it to .itemtext.on and it should work.
I am trying to do append() function. i have a table of data. i run a loop to first remove text in cell then i will append a new tag.this usecase is to create a progress bar. for an example
data sample inside cell is e.g 39% 39% 82% etc etc
let cf_percent;
let cf_regex;
for(let i = 0 ;i < tbl[0].length;i++){
cf_percent = tbl[0][i].innerHTML
cf_regex = cf_percent.replace(/[`~%]/gi, '');
console.log(cf_regex)
//Clear fields
tbl[0][i].innerHTML = ''
tbl[0][i].append('<p>Textfield</p>');
}
It should return texfield but instead, it is returning '<p> textfield </p>' in table cell.it should return textField. i have tried .html() but this does not work for this usecase.
in d3.js append function appends a new element with the specified name as the last child of each element in the current selection, returning a new selection containing the appended elements.
So to append p element use: tbl[0][i].append("p") and to set text use .text() further: e.g.
tbl[0][i].append("p").text("Textfield")
I will suggest to use:
let cf_percent;
let cf_regex;
for(let i = 0 ;i < tbl[0].length;i++){
cf_percent = tbl[0][i].innerHTML
cf_regex = cf_percent.replace(/[`~%]/gi, '');
console.log(cf_regex)
//Clear fields
tbl[0][i].innerHTML = ''
tbl[0][i].innerHTML += `<p>Textfield</p>`;
}
You have 2 options. The first is along what you are trying to do where you set the innerHTML to a string. The second is actually generating an element and then appending it. Your current scenario seems to be mixing the two.
const div1 = document.getElementById('sample1'),
div2 = document.getElementById('sample2'),
p = document.createElement('p');
div1.innerHTML = '<p>This set the innerHTML</p>';
p.innerText = 'This is a p appended to a div';
div2.appendChild(p);
#sample1 {
background-color: red;
}
#sample2 {
background-color: yellow;
}
<div id="sample1"></div>
<div id="sample2"></div>
I don't know how to solve that problem, If anyone knows how to solve that then please let me know. If required, then I will send the code personally to figure out the mistakes.
There are two containers: left and right. In Left, there is a from where I take values of title, description and status(active/inactive) from text boxes and radio buttons(active/inactive). Then after pressing submit button all values are filled in table of right-container with edit and delete button attached after clicking submit button every time. I want to delete specific row where delete button is clicked. But I don't know how to access that button while onclick function(doDelete()) is same in all the buttons.
function fillData() {
var table = document.getElementById("myTable");
var counter = table.querySelectorAll('tr').length;
var key = counter;
var row = table.insertRow(counter);
row.id = "row-" + key;
var titleCell = row.insertCell(0);
var descCell = row.insertCell(1);
var statusCell = row.insertCell(2);
var actionCell = row.insertCell(3);
var editButton = document.createElement("button");
editButton.innerText = "Edit";
editButton.id = "edit-" + key;
editButton.setAttribute("onclick", "doEdit()");
var delButton = document.createElement("button");
delButton.innerText = "Delete";
delButton.id = "delete-" + key;
delButton.setAttribute("onclick", "doDelete()");
titleCell.innerHTML = document.getElementById("panel-title").value;
descCell.innerHTML = document.getElementById("panel-description").value;
statusCell.innerHTML = (function () {
var radios = document.getElementsByName("status");
for (i = 0, len = radios.length; i < len; i++) {
if (radios[i].checked) {
return radios[i].value;
}
}
}());
actionCell.appendChild(editButton);
actionCell.appendChild(delButton);
var delBtnArr = document.querySelectorAll('input[type="button"]');
console.log(delBtnArr);
}
Actual Results: After pressing delete button, the whole rows are deleted.
Expected Results: After pressing delete button, the specific row is deleted where button is clicked.
Javascript also sends the assosiated event as parameter and in this way you can get id by using event utilities. You can get the clicked button id as below. By getting that id I think you can also get the associated row. After that you can delete the row.
doDelete(event){
let clickedButtonId = e.target.id;
//get row id. I think you can get it.
document.removeElement(rowId);
}
Event Delegation
Bind/Register the Ancestor to Event
Dynamically added tags cannot bind to an event handler/listener, only tags that have existed since the page was loaded can. So for multiple dynamically added tags such as the buttons you have to find an ancestor tag that they all share incommon and bind it to whatever event you need to listen for. For your buttons it can be the closest ancestor table* (recommended) to the furthest window:
// On-event property. ALWAYS PASS THE EVENT OBJECT
table.onclick = function(event) {...
OR
// Event Listener. Abbreviating the [Event Object][2] is OK, but you must be consistent.
table.addEventListener('click', function(e) {...
Do not use on-event attributes <button onclick="func()"...
✱Technically the closest ancestor is tbody even if you didn't add it to the table, the browser will add it by default.
Use Event.target and Event.currentTarget Properties
Remember to pass the Event Object because you'll need it to...
...find out which button you actually clicked with event.target property.
...get a reference to the table with event.currentTarget property.
...possibly prevent default behavior such as stopping a form from submitting to a server with event.preventDefault() method.
Review the demo, it will have specific details on the event handlers.
Demo
Details commented in demo
var table = document.querySelector("table");
document.forms[0].onsubmit = fillData;
/*
This onevent property handler has two functions note it is bound
to the table NOT the buttons.
There's two conditionals and they only focus on classNames of
either .del or .edit. Once it's determined if the clicked tag has
one of these classes then the appropriate function is called.
If neither class was clicked there's no opportunity for anything
else to act on the click because both conditionals end with
return false thereby terminating the event handler.
*/
table.onclick = function(e) {
if (e.target.className === 'del') {
delRow(e);
return false;
}
if (e.target.className === 'edit') {
editRow(e);
return false;
}
};
function fillData(e) {
var ui = e.target.elements;
e.preventDefault();
var idx = table.rows.length;
var row = table.insertRow();
row.id = 'r-' + idx;
var cell1 = row.insertCell(0);
var data1 = ui.title.value;
cell1.textContent = data1;
var cell2 = row.insertCell(1);
var data2 = ui.desc.value;
cell2.textContent = data2;
var cell3 = row.insertCell(2);
var data3 = ui.chk.checked ? 'Active' : 'Inactive';
cell3.textContent = data3;
var cell4 = row.insertCell(3);
var btns = `
<button class='edit'>📝</button>
<button class='del'>❌</button>`;
cell4.innerHTML = btns;
}
/*
Reference the .closest() row from clicked button
Get that row's id and split() it at the dash and pop() the number.
Then get a reference to the bound ancestor (table) and deleteRow() with the new number you just got.
*/
function delRow(e) {
var row = e.target.closest('tr');
var idx = row.id.split('-').pop();
e.currentTarget.deleteRow(idx);
}
/*
Same as before get the index number from the closest row's id.
Reference the table and use the .rows property and number.
This reference will now allow you to use the .cells property.
Use the .cells property to toggle the contenteditable attribute
on the first three cells.
*/
function editRow(e) {
var row = e.target.closest('tr');
var idx = row.id.split('-').pop();
var R = e.currentTarget.rows[idx];
for (let c = 0; c < 3; c++) {
var cell = R.cells[c];
if (cell.hasAttribute('contenteditable')) {
cell.removeAttribute('contenteditable');
} else {
cell.setAttribute('contenteditable', true);
}
}
}
body {
font: 400 16px/25px Consolas;
display: flex;
justify-content: space-between;
}
fieldset {
width: fit-content
}
input,
label,
textarea {
font: inherit
}
input,
label,
button {
display: inline-block;
height: 25px;
}
#title {
width: 27.5ch;
}
#chk {
display: none;
}
#chk+label::after {
content: '\2610';
font-size: 20px;
vertical-align: middle;
}
#chk:checked+label::after {
content: '\2611';
}
[type='reset'] {
margin-left: 5%
}
td {
min-width: 60px;
border-bottom: 1px solid #000;
height: 25px;
}
tr td:last-child {
border-bottom-color: transparent;
}
button {
width: 35px;
text-align: center;
}
<form id='data'>
<fieldset>
<legend>Enter Data</legend>
<input id='title' type='text' placeholder='Title'><br>
<textarea id='desc' rows='3' cols='25' placeholder='Description'></textarea><br>
<input id='chk' type='checkbox'>
<label for='chk'>Active </label>
<input type='reset'>
<input type='submit'>
</fieldset>
</form>
<hr>
<table></table>
I'm making a react component for selecting and deselecting tags. For this is want the tags to be shown in a table, with a checkbox next to them.
My issue is that I can't set the text part of it, when creating them through JavaScript. Neither through innerHTML nor innerText. It shows in the inspector, but not in my browser (Chromium).
So far my method looks like this:
generateTagTable() {
let tbl = document.getElementById('tagTable')
tbl.className = 'tagTable'
let tbdy = document.createElement('tbody')
for (let i = 0; i < this.allTags.length; i++) {
let tr = document.createElement('tr')
let td = document.createElement('td')
let div = document.createElement('div')
let cb = document.createElement('input')
div.className = 'checkContainer'
cb.id = 'checkTd'
cb.type = 'checkbox'
cb.innerText = 'Tag'
div.appendChild(cb)
td.appendChild(div)
tr.appendChild(td)
tbdy.appendChild(tr)
}
tbl.appendChild(tbdy)
}
Which results in this:
I know that I can achieve my goal by doing the this:
generateTagTable() {
let tbl = document.getElementById('tagTable')
tbl.className = 'tagTable'
let tbdy = document.createElement('tbody')
for (let i = 0; i < this.allTags.length; i++) {
let tr = document.createElement('tr')
let td = document.createElement('td')
td.className = 'checkContainer'
td.innerHTML = '<div class="flexcenter"><input type="checkbox" name="tagCheck"/>' + this.allTags[i] + '</div>'
tr.appendChild(td)
tbdy.appendChild(tr)
}
tbl.appendChild(tbdy)
}
But that leaves me with problem when trying to assign the onClick, and generally leaves me with less control I feel.
The result of the working one is this, and what i'm trying to achieve is this:
Checkboxes (input elements in general) don't have content (they're void elements), so innerText and textContent and innerHTML don't have any function in relation to them.
If you want text next to your checkbox, the usual thing is to wrap a label around it. In markup, that would be:
<label>
<input type="checkbox">
Text
</label>
Adjusting your code, you might do:
// ...
let td = document.createElement('td')
let div = document.createElement('div')
let label = document.createElement('label') // *** (added)
label.innerText = 'Tag' // *** (moved and modified)
let cb = document.createElement('input')
div.className = 'checkContainer'
cb.id = 'checkTd'
cb.type = 'checkbox'
label.insertBefore(cb, label.firstChild); // *** (added)
div.appendChild(label) // *** (modified)
// ...
That said, it would be much simpler via markup (tr.innerHTML = ...), and browsers are very fast parsing markup.
// ...
let tr = document.createElement('tr')
tr.innerHTML = `
<td>
<div class="checkContainer">
<label>
<input type="checkbox" id="checkTd">
Tag
</label>
</div>
</td>`;
tbdy.appendChild(tr)
// ...
Side note: As it stands, your code creates multiple input elements with the same id (checkTd). That's invalid, only one element in a document can have that id. Depending on what you're doing, you may not need an id at all, or you may need to add a suffix to it (perhaps using i) to make it unique.
I hope you can read my code, I have an XML file on my localhost and I would like it to pull the title and year from that file (it currently has Title, Year, Artist, Price, and Country) while maintaining the H1 and button on the page.
The H1 text and button disappear onClick and I would like it to remain on the same page as the results.
<h1>Show the Album list</h1>
<script>
xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","cd_catalog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
function CdCatalog()
{
document.write("<table border='1'><th>TITLE</th><th>YEAR</th>");
var x=xmlDoc.getElementsByTagName("CD");
for (i=0;i<x.length;i++)
{
document.write("<tr><td>");
document.write(x[i].getElementsByTagName("TITLE")[0].childNodes[0].nodeValue);
document.write("</td><td>");
document.write(x[i].getElementsByTagName("YEAR")[0].childNodes[0].nodeValue);
document.write("</td></tr>");
}
document.write("</table>");
}
</script>
The problem is that you're using document.write for a button's click event, which is after the document's been closed...which means it overwrites everything. Since your page is simple and only has an <h1> and button, it looks like only those things are being hidden, but it would be everything on the page (if you had more).
The solution is to use .appendChild and/or .innerHTML to add the content dynamically. I'll provide a solution in a minute :)
UPDATE:
Since you're using tables, you might as well use the native methods .insertRow and .insertCell that make table creation much easier. Here's an example of what you could use overall:
function CdCatalog(xmlDoc) {
var table = document.createElement("table");
var thead = table.createTHead(); // Where "header" rows go
// `insertRow` creates a <tr> element and appends it to `thead` automatically, returning the element
var tr = thead.insertRow(-1);
var td = document.createElement("th"); // No special method for creating "th" elements
td.innerHTML = "TITLE"; // Set its inner content
tr.appendChild(th); // Add it to the row (which is in the header)
td = document.createElement("td");
td.innerHTML = "YEAR";
tr.appendChild(td);
var x = xmlDoc.getElementsByTagName("CD");
// "tbody" is where a table's content goes, whether you do this explicitly or not
var tbody = table.tBodies[0];
for (var i = 0; i < x.length; i++) {
tr = tbody.insertRow(-1);
// `insertCell` creates a <td> element and appends it to `tr` automatically, returning the element
td = tr.insertCell(-1);
td.innerHTML = x[i].getElementsByTagName("TITLE")[0].childNodes[0].nodeValue;
td = tr.insertCell(-1);
td.innerHTML = x[i].getElementsByTagName("YEAR")[0].childNodes[0].nodeValue;
}
// Actually add the table to the DOM (the <body> element in this case)...you can specify where else to put it
document.body.appendChild(table);
}
// Make sure DOM is ready for manipulation
window.onload = function () {
var btn = document.getElementById("button_id"); // Whatever your button is
// Bind the "click" event for the button
btn.onclick = function () {
// Make your AJAX request
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "cd_catalog.xml", false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;
// Pass the result to the function, instead of making everything global and sharing
CdCatalog(xmlDoc);
};
};
And unless I'm mistaken, you can't nest <td> inside of <table>...you must nest them in <tr>...which are nested in <table>
Don't use document.write. It will remove everything you have inside the tag since the document is closed. Do this instead:
document.body.innerHTML += "<td>blah</td>"
-or-
var td = document.createElement("td");
td.innerHTML = "blah"
document.querySelector("table").appendChild(td);
Demo: http://jsfiddle.net/DerekL/AHZ4C/