Well this is a thing i need help of some one who know how to get the element from a foreach that innerHTML into a table data that comes from a data base, i did it by this way but it is not the most optimize way, so i need help to do it by other way, i add an onclick event with javascript and the only thing i make by doing this is that the only button that works is the one on top of the table or the bottom button of the table.
This is the way i make it work:
//Preload
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld(
"electron", {
printmovieonpreload: (results) => ipcRenderer.on("send-movie", (event, results) => {
mylist.innerHTML = " "
results.forEach(elements => {
mylist.innerHTML += `<tr><td> ${elements.movie-name} </td>
<td> ${elements.movie-duration} min </td><td><button id="btn" value="${elements.id-movie}" "/*this get all the onclick events of the page*/${onclick = deletefromtable}" type="button" class="fas cli fa-trash-alt"></button></td>
</tr>`;
});
})
});
async function deletefromtable(e) {
/* this is were i filter from id of the onclick event and how i reduce the value of the button to delete it*/
if (e.srcElement.id == "btn") {
const obj = {
id: e.srcElement.value
}
await ipcRenderer.invoke('delete_movie', obj);
}
}
It's better to not do this by string interpolation, but by letting the browser (in Electron's terms, Chromium) create elements of the wanted type and then setting their contents and appending them to the parent. This can (and should) be done like so:
// inside your forEach function
var tr = document.createElement ("tr");
var td1 = document.createElement ("td");
var td2 = document.createElement ("td");
var td3 = document.createElement ("td");
var btn = document.createElement ("button");
td1.textContent = elements ["movie-name"];
td2.textContent = elements ["movie-duration"] + " min";
btn.id = "btn";
btn.classList.add ("fas", "cli", "fa-trash-alt");
btn.type = "button";
btn.value = elements ["id-movie"];
btn.onclick = delete_from_table;
td3.appendChild (btn);
tr.appendChild (td1);
tr.appendChild (td2);
tr.appendChild (td3);
myList.appendChild (tr);
For this to work, however, you must meet the following conditions:
The index strings for the elements array must be correct. I'm pretty sure that your code using string interpolation would throw errors, because - is an operator and cannot be used when referencing indices using the dot operator.
delete_from_table must be a function, not a string. Since it is not included in the code snippet you have posted, I cannot verify if my solution would work based off of that. However, it seems that you would rather like to bind updatestatuscero to the onclick event of the button. If so, simply replace delete_from_table with this function's name.
Just some more tips regarding general JavaScript:
Use braces wherever possible to make the code more intelligible. For example, you could have written your code like so:
contextBridge.exposeInMainWorld(
"electron", {
printmovieonpreload: (results) => {
ipcRenderer.on("send-movie", (event, results) => {
mylist.innerHTML = " "
results.forEach(elements => {
mylist.innerHTML += `<tr><td> ${elements.movie-name} </td>
<td> ${elements.movie-duration} min </td><td><button id="btn" value="${elements.id-movie}" "/*this get all the onclick events of the page*/${onclick = delete_from_table}" type="button" class="fas cli fa-trash-alt"></button></td>
</tr>`;
});
});
}
}
});
This way, the scope of the functions can be understood at first glance. Both you and any other person having to work with this code in the future will be very pleased if you keep it organised.
When using string interpolation, you can execute operations. This is why I stated that - is not valid in an index above. The reason for this is that any expression inside ${} in an interpolated string will be evaluated and its return value will be included in the string. Thus,
var testObject = {
"test-prop": "def"
};
var stringA = `abc ${testObject.test-prop} ghi`; // Throws: ReferenceError, prop is not defined
var stringB = `abc ${testObject["test-prop"]} ghi`; // yields "abc def ghi"
What the expression inside stringA tries to do is to actually subtract prop from testObject.test, neither of which are defined.
Also, with string interpolation, you have executed an assignment (${ onclick = delete_from_table}) which caused the new variable onclick to be defined, the method to be (correctly) written to the HTML, but not bound to the buttons' onclick event! Instead, you could have done onclick="${delete_from_table}" just like you did with the value property.
Don't ever insert HTML by using element.innerHTML! Take a look at MDN -- this method has some pretty unexpected implications. Better sanitise your HTML first, use element.insertAdjacentHTML() or simply create elements and add them to a parent element by using element.appendChild() as I showed you above. This way is also, in my opinion, but tastes differ, the cleanest way possible.
Related
<div id="">
<span>{{msg}}</span>
</div>
Let's think msg is variable of JavaScript and now I want to get the parent tag of {{msg}} and push a new value by innerHTML, here {{msg}} working as an identity.
demo JavaScript example:
<script>
var msg = "This is update data";
{{msg}}.parentElement.innerHTML=msg;
</scritp>
This is not actual JavaScript code, only for better understanding.
You can use jquery easily to find that element and then replace the text
var msg = "This is update data";
$(`span:contains(${msg})`).html("Its New");
In javascript:
var spanTags = document.getElementsByTagName("span");
var msg = "This is update data";
var found;
for (var i = 0; i < spanTags.length; i++) {
if (spanTags[i].textContent == msg) {
found = spanTags[i];
break;
}
}
Now, you have found that element in found and you can now change its text
if (found) {
found.innerHTML = "New text";
}
The simplest approach is to treat the entire document as a string and then re-parse it when you're done.
The .innerHTML property is both an HTML decompiler and compiler depending on weather you're reading or writing to it. So for example if you have a list of variables that you want to replace in your document you can do:
let vars = {
msg: msg, // pass value as variable
test_number: 10, // pass value as number
test_str: 'hello' // pass value as string
};
let htmlText = document.body.innerHTML;
// find each var (assuming the syntax is {{var_name}})
// and replace with its value:
for (let var in vars) {
let pattern = '\\{\\{\\s*' + var + '\\s*\\}\\}';
let regexp = new RegExp(pattern, 'g'); // 'g' to replace all
htmlText = htmlText.replace(regexp, vars[var]);
}
// Now re-parse the html text and redraw the entire page
document.body.innerHTML = htmlText;
This is a quick, simple but brutal way to implement the {{var}} syntax. As long as you've correctly specified/designed the syntax to make it impossible to appear in the middle of html tags (for example <span {{ msg > hello </ }} span>) then this should be OK.
There may be performance penalties redrawing the entire page but if you're not doing this all the time (animation) then you would generally not notice it. In any case, if you are worried about performance always benchmark your code.
A more subtle way to do this is to only operate on text nodes so we don't accidentally mess up real html tags. The key to doing this is to write your own recursive descent parser. All nodes have a .childNodes attribute and the DOM is strictly a tree (non-cyclic) so we can scan the entire DOM and search for the syntax.
I'm not going to write complete code for this because it can get quite involved but the basic idea is as follows:
const TEXT_NODE = 3;
let vars = {
msg: msg, // pass value as variable
test_number: 10, // pass value as number
test_str: 'hello' // pass value as string
};
function walkAndReplace (node) {
if (node.nodeType === TEXT_NODE) {
let text = node.nodeValue;
// Do what you need to do with text here.
// You can copy the RegExp logic from the example above
// for simple text replacement. If you need to generate
// new DOM elements such as a <span> or <a> then remove
// this node from its .parentNode, generate the necessary
// objects then add them back to the .parentNode
}
else {
if (node.childNodes.length) {
for (let i=0; i<node.childNodes.length; i++) {
walkAndReplace(node.childNodes[i]); // recurse
}
}
}
}
walkAndReplace(document.body);
this is my code:
function newTodo() {
const text = prompt('What needs to be done?').trim();
if (!!text) {
id += 1;
const todoId = 'todo' + id;
// checkbox
const todoCheckbox = document.createElement('input');
todoCheckbox.type = 'checkbox';
todoCheckbox.id = todoId;
todoCheckbox.classname = classNames.TODO_CHECKBOX;
todoCheckbox.setAttribute('onclick', `handleCheck(${todoId})`);
...
} else {
alert('Please enter a valid todo');
}
}
function handleCheck(todoId) {
console.log('checked!', todoId);
}
but handleClick is logging the html element the function is attached to:
<input type="checkbox" id="todo2" onclick="handleCheck(todo2)">
I wanted to log the id only, so i am expecting it to be:
todo2
what am i doing wrong?
Your handler:
onclick="handleCheck(todo2)"
attempts to reference a global variable named "todo2" and pass it to the handleCheck function. Because (confusingly) element IDs are automatically added to the global object, this results in the element you just created being passed to the function.
Surround the todoId in quotes instead, to ensure it gets passed as a string:
todoCheckbox.setAttribute('onclick', `handleCheck('${todoId}')`);
But, it would be significantly more elegant to add the handler properly using Javascript instead of an inline HTML attribute:
todoCheckbox.onclick = () => handleCheck(todoId);
Dynamic IDs are a code smell, though - they're rarely necessary. If you need data unique for each element, you might consider using data- attributes, or a Map.
It should be <input type="checkbox" id="todo2" onchange="handleCheck(todo2)">
I have the following code where I want to pass an order object to deleteOrder function using template literal.
function updateUI() {
let orderItems = orders.map(function(order){
return `<li class="order">
<label>${order.name}</label>
<p>${order.coffee}</p>
<button onclick="deleteOrder(\"${order.key}\")">Remove</button>
</li>`
})
ordersList.innerHTML = orderItems.join('')
}
SOLUTION:
function updateUI() {
let orderItems = orders.map(function(order){
return `<li class="order">
<label>${order.name}</label>
<p>${order.coffee}</p>
<button onclick="deleteOrder('${order.key}')">Remove</button>
</li>`
})
ordersList.innerHTML = orderItems.join('')
}
Inline handlers are bad practice and difficult to manage, as you're finding out. Also, any way you represent an object in HTML, there's no way to make it so that it's === to the original object (so, for example, you couldn't use includes to check to see if an array contains that object). Attach the handler properly using Javascript instead so that order can be referenced via its closure:
let orderItems = orders.map((order) => {
const li = document.createElement('li');
li.className = 'order';
li.innerHTML = `
<label>${order.name}</label>
<p>${order.coffee}</p>
<button>Remove</button>`;
li.children[2].addEventListener('click', () => {
deleteOrder(order);
});
return li;
});
If you don't want to use .children[index], if your li has only one button, you can select it with querySelector:
li.querySelector('button').addEventListener...
Note that this will result in orderItems returning an array of elements rather than an array of strings, so rather than using the result to assign to some innerHTML, call appendChild on the container to append the li. For example:
const lis = orderItems(orderArr);
lis.forEach((li) => {
container.appendChild(li);
});
If you want to pass order.key to deleteOrder instead of passing order, then replace
deleteOrder(order);
with
deleteOrder(order.key);
Although inline handlers shouldn't be used, the way to do it would be to use double quotes around the whole onclick attribute, and single quotes around the order.key string, making sure to escape any single or double quotes in order.key:
const escapedKey = order.key.replace(/['"]/g, '\\$&');
// ...
<button onclick="deleteOrder('${escapedKey}')">Remove</button>
But the escaping is ugly and inelegant - attach the handler with Javascript instead.
I know the question sounds strange, but it's really very simple. I have the following function which isn't working:
function start40Counter(counter40_set){console.log(counter40_set);
var gid = counter40_set[0];
var seat = counter40_set[1];
var suits = counter40_set[2];
var cont = "";
$.each(suits, function (num, suit) {
cont += "<a class='suitpick' onClick='pickSuit(counter40_set);'><img src='"+base+"images/someimg.png' title='Odaberi' /></a>";
});
$('#game40_picks').html(cont);
}
counter40_set is [10, 3, ["H", "S"]]. The part of the function that fails is the part this:
onClick='pickSuit(counter40_set);'
It says that counter40_set is not defined. I understand that. This wouldn't even work if counter40_set was a simple string instead of an array. If I try onClick='pickSuit("+counter40_set+");' I get a different error, saying H is not defined. I get this too, the array is rendered and JS doesn't know what H and S are.
I also tried passing the array elements (counter40_set[0] etc) individually but it still fails with the last element (["H", "S"]).
So, how do I pass this data to the onClick function in this case? There must be a more elegant way than concatenating the whole thing into a string and passing that to the function?
Btw, this is a simplified version. What I should really be passing in every iteration is [suit, counter40_set] so that each link chooses a different suit. I'm asking the simplified question because that will be enough to send me down the right path.
It cannot work,because the context is lost and thus "counter40_set" is not set.
To fix it simply use jquery for the onlick as well:
$('#game40_picks').empty(); // get rid of everything
$.each(suits, function (num, suit) {
var line = $("<a class='suitpick'><img src='"+base+"images/"+cardsuits[suit].img+"' title='Odaberi "+cardsuits[suit].name+"' /></a>");
line.click(function(ev){
ev.preventDefault(); // prevent default click handler on "a"
pickSuit(counter40_set);
});
$('#game40_picks').append(line);
});
this way the "counter40_set" is available for the click function.
You shouldn't use the onClick HTML attribute. Also, using DOM functions to build nodes saves the time it takes jQuery to parse strings, but basically the method below is to create the element and attach a click event listener and then append it to the specified element.
function start40Counter(event){console.log(event.data.counter40_set);
var counter40_set = event.data.counter40_set;
var gid = counter40_set[0];
var seat = counter40_set[1];
var suits = counter40_set[2];
var cont = "";
$.each(suits, function (num, suit) {
var link = document.createElement('a');
link.className = 'suitpick';
$(link).on('click', {counter40_set: counter40_set}, start40Counter);
var img = document.createElement('img');
img.src= base + "images/" + cardsuits[suit].img;
img.title = 'Odaberi ' + cardsuits[suit].name;
link.appendChild(img);
$('#game40_picks').append(link);
});
}
Not tested but it might work out of the box.
Lets say that I have a element reference obtained ala...
var x=document.getElementById("ID123");
Now I want to include some function of x on a dynamically created html element ala...
myWin.document.write("<input type='button' data-doit=????? value='Do It' onclick='window.doit(?????);'/>");
so that function doit() can recreate the original reference x
function doit()
{
var x = ?????;
}
Right now I'm doing this by obtaining a numeric index of x as found via getElementsByTagName and passing that as a parameter to doit().
Is there some way to do this?
I would probably pass the ID of x to doit(), which would require doit() to look something like this:
function doit(id) {
var x = document.getElementById(id);
// etc
}
As far as creating the input element goes, there a couple of ways to do this. This is similar to your current solution (but non-standard):
myWin.document.write('<input ... onclick="doit(' + x.id + ');" />');
The standard solution is to create your new input element using the DOM API:
var input = myWin.document.createElement('input');
input.type = 'button';
// other properties
input.onclick = function () {
doit(x.id);
};
// insert new element
document.getElementById("myform").appendChild(input);
(Note that inserting the element is a little more complicated in this situation - you have to programmatically find its location in the DOM).
Finally, if you're using jQuery, you might do something this:
var input = $('<input type="button" etc />').click(function () {
doit(x.id);
});
$('#myform').append(input);