How to pass variable into a onclick function - javascript

I am wanting to try and pass record.ItemID to my onclick = buy() function. But I am getting errors like "Uncaught SyntaxError: Unexpected end of input"
I have tried \"record.ItemID \" but that of course just passes the literal string of result.name
I have also tried (\'' + record.ItemID + '\') but get the same Syntax error
function showShop(items) {
let tableContent = "<tr class='orderTitle'><td =imgTable></td><td id = contentTable ></td></tr>\n";
let odd = true;
const addRecord = (record) => {
tableContent += odd ? "<tr class='orderOdd'>" : "<tr class='orderEven'>";
odd = !odd;
tableContent += "<td>" + "<img id = image src="+ "http://redsox.uoa.auckland.ac.nz/ms/MuseumService.svc/shopimg?id=" + record.ItemId + " />" + "</td><td id = content>" + record.Description + "<td><button onclick='buy("+ record.ItemId +")'/> Buy </button></td>";
}
items.forEach(addRecord)
document.getElementById("shop").innerHTML = tableContent;
}
function buy(item){
window.open('http://redsox.uoa.auckland.ac.nz/mss/Service.svc/buy?id='+ item,'_self');
}

I'm not sure if this will solve your problem but it looks like you're mixing up 's and "s.
onclick='buy('record.ItemId')'
You are terminating the onclick attribute right after buy(.
You may need to do something like:
onclick='buy(" + record.ItemId + ")'
Generally speaking though, if you have to build up HTML in strings, you are better off string interpolation. It makes it easier to read and is less prone to these types of issues.
Example:
const html = `<button onclick="buy(${record.ItemId})">Click</button>`;

It looks like you're trying to build up some HTML content to put into a table, and you want some behaviour attached to a button inside the table so that it opens a new window when you click on it.
There are a number of different approaches to what you're trying to do which would be safer in production code, so while you've got some answers to your specific question, please consider these alternative approaches which are more idiomatic:
You could use a link (<a>) instead of a button, and use CSS to make the link look like a button. This avoids needing a click handler at all.
You could use data attributes to store the record in a safe way, then access it from the click event, e.g. e.target.dataset.recordId.
You could use jQuery or a similar toolkit to create the button, then attach a function to the button as a click handler.
When you create HTML directly like you are doing in your question, you're opening your code up to code injection, where someone malicious could craft data that could steal private information from users of your site. It's much safer to use a library to construct your HTML directly rather than building it up in strings.

Really you're much better off separating out your inline JS and using event listeners to target classes on your elements.
Here's a quick example to show you how you might achieve that:
const records = [{ itemId: 1 }, { itemId: 2 }, { itemId: 3 }];
const imgRoot = 'https://dummyimage.com/30x30/676767/fff.png?id=';
// `map` iterates over the array and produces one element of HTML per record
// We use a class on the button to identify it, and a data attribute
// button to hold the itemId
const html = records.map(({ itemId }) => {
return `
<div class="itemWrapper">
<img class="item" src="${imgRoot}${itemId}" />
<button data-itemid="${itemId}" class="buyRecord">Buy record</button>
</div>
`;
});
document.querySelector('.root').innerHTML = html.join('');
// We grab the buttons and iterate over them attaching
// event listeners that call `handleBuy` when the button is clicked
const buyButtons = document.querySelectorAll('.buyRecord');
buyButtons.forEach(button => button.addEventListener('click', handleBuy, false));
function handleBuy(e) {
// Destructure the itemid from the dataset of the button
// click event
const { target: { dataset: { itemid } } } = e;
console.log(itemid);
}
<div class="root" />
Documentation
map
Data attributes
Template literals
Destructuring assignment

The General format of onclick is
onclick="function_name(variable)"
For this case you can do something like this:
tableContent += '<td>' + '<img id = image src="http://redsox.uoa.auckland.ac.nz/ms/MuseumService.svc/shopimg?id=' + record.ItemId + '" /></td><td id="content">' + record.Description + '<td><button onclick="buy('+record.ItemId+')"> Buy </button></td>';

Related

Anonymous function in dynamically generated list producing SyntaxError

I want to place an onclick event inside a dynamically generated list. I can't use it as it is, like updateRoomID(arg), because it would fire immediately. So I placed it inside an anonymous function, as advised by various sources online: function (){updateRoomID(arg)}. But this results in: "Uncaught SyntaxError: Unexpected token (". Developer tools says the problem is at function().
The section of code it's in:
socket.onmessage = function(event) {
var msg = JSON.parse(event.data);
for (let i = 0; i < msg.length; i++) {
if (msg[i].beingserved == false) {
listRooms.innerHTML += '<li id=' + msg[i].roomid +
// Problem on following line.
' onclick=' + function () { updateRoomID(msg[i].roomid) } +
'>' +
'<a href="#">' +
msg[i].roomid +
'</a></li>';
} else {
document.getElementById(msg[i].roomid).remove();
};
};
};
I've tried it with the function as a string inside quotations marks: <li id=' + msg[i].roomid +' onclick="function(){updateRoomID(msg[i].roomid)}">'. I've tried placing the onclick handler in href link instead, and also replaced it with addEventListener. But I got the same error with these attempts.
If I try function(){updateRoomID(arg)}() with the parentheses behind, it fires immediately as expected.
I've been looking through it all day and can't figure out where the syntax error is. I'm quite unfamiliar with JavaScript. What am I doing wrong?
You can't put a function in the onclick attribute. It contains JavaScript source code that should be executed.
What you should do in this case is put the function call as a string, but substitute in the value of the argument.
Using a template literal makes this easier.
listRooms.innerHTML += `
<li id="${msg[i].roomid}" onclick="updateRoomId(${msg[i].roomid})">
${msg[i].roomid}
</li>`;

Passing argument to on click event of dynamically generated element

I am trying to pass arguments to onclick event of dynamically generated element. I have already seen the existing stackoveflow questions but it didn't answer my specific need.In this existing question , they are trying to access data using $(this).text(); but I can't use this in my example.
Click event doesn't work on dynamically generated elements
In below code snippet, I am trying to pass program and macroVal to onclick event but it doesn't work.
onClickTest = function(text, type) {
if(text != ""){
// The HTML that will be returned
var program = this.buffer.program;
var out = "<span class=\"";
out += type + " consolas-text";
if (type === "macro" && program) {
var macroVal = text.substring(1, text.length-1);
out += " macro1 program='" + program + "' macroVal='" + macroVal + "'";
}
out += "\">";
out += text;
out += "</span>";
console.log("out " + out);
$("p").on("click" , "span.macro1" , function(e)
{
BqlUtil.myFunction(program, macroVal);
});
}else{
var out = text;
}
return out;
};
console.log of out give me this
<span class="macro consolas-text macro1 program='test1' macroVal='test2'">{TEST}</span>
I have tried both this.program and program but it doesn't work.
Obtain values of span element attributes, since you include them in html:
$("p").on("click" , "span.macro" , function(e)
{
BqlUtil.myFunction(this.getAttribute("program"),
this.getAttribute("macroVal"));
});
There are, however, several things wrong in your code.
you specify class attribute twice in html assigned to out,
single quotes you use are not correct (use ', not ’),
quotes of attribute values are messed up: consistently use either single or double quotes for attribute values
var out = "<span class='";
...
out += "' class='macro' program='" + program + "' macroVal='" + macroVal + ;
...
out += "'>";
depending on how many times you plan to call onClickTest, you may end up with multiple click event handlers for p span.macro.

Adding html while iterating over jquery object

Is there a better way of inserting somewhat complex html into a page other than the way I'm doing now? :
function display(friends) {
$(".row").empty();
$.each(friends, function(index, friend) {
var html = '<div class="profileImage" style="float:left;padding:20px; width:200px">';
html += '<a href="/app/click/' + friend.id + '">';
html += '<img id="' + friend.id + ' " src="https://graph.facebook.com/' + friend.id + '/picture?width=200&height=200 " />';
html += '</a>';
html += '</div>';
$(".row").append(html);
});
Currently I have a list of facebook friends which are styled nicely. When a user searches through the friends, the entire content block is emptied and the result is appended (i'm using autocomplete). However the design could change and get more complex so i'm looking for a scalable way of doing what I have above.
Instead of creating the html inside the javascript, is there a smarter way of doing this? Perhaps with $.load() and passing each friend as an argument? But that seems very slow and server intensive if you have to list 100 friends.
One good way to go would be to use a templating engine, handlebars (as mentioned in the prev answer) is one of them. You could create your own as well if your scenario is simple as this. And another key thing is not to use append inside the loop, instead construct them to a temp array and add it to the DOM in the end. If your list is big and appending to the dom in the array can be expensive.
Add the template html with a placeholder for friendId
<script type="text/html" id="template">
<div class = "profileImage" style = "float:left;padding:20px; width:200px">
<a href = "/app/click/{{friendId}}">
<img id = "{{friendId}}" src = "https://graph.facebook.com/{{friendId}}/picture?width=200&height=200 " />
</a>
</div>
</script>
And
var $template = $('#template'),
$row = $('.row');
function display(friends) {
var rows = [];
$.each(friends, function (index, friend) {
var templateHtml = $template.text().replace(/{{friendId}}/g, friend.id);
rows.push(templateHtml);
});
$row.html(rows); //Append them in the end
}
Demo
You could use $.map as well.
var $template = $('#template'),
$row = $('.row');
function display(friends) {
var rows = $.map(friends, function (friend) {
var templateHtml = $template.text().replace(/{{friendId}}/g, friend.id);
return templateHtml;
});
$row.html(rows);
}
A scalable solution would be to use a template engine and make the server returns JSON response.
Take a look at Handlebars.js http://handlebarsjs.com/

Uncaught ReferenceError: function is not defined with onclick

I'm trying to make a userscript for a website to add custom emotes. However, I've been getting a lot of errors.
Here is the function:
function saveEmotes() {
removeLineBreaks();
EmoteNameLines = EmoteName.value.split("\n");
EmoteURLLines = EmoteURL.value.split("\n");
EmoteUsageLines = EmoteUsage.value.split("\n");
if (EmoteNameLines.length == EmoteURLLines.length && EmoteURLLines.length == EmoteUsageLines.length) {
for (i = 0; i < EmoteURLLines.length; i++) {
if (checkIMG(EmoteURLLines[i])) {
localStorage.setItem("nameEmotes", JSON.stringify(EmoteNameLines));
localStorage.setItem("urlEmotes", JSON.stringify(EmoteURLLines));
localStorage.setItem("usageEmotes", JSON.stringify(EmoteUsageLines));
if (i == 0) {
console.log(resetSlot());
}
emoteTab[2].innerHTML += '<span style="cursor:pointer;" onclick="appendEmote(\'' + EmoteUsageLines[i] + '\')"><img src="' + EmoteURLLines[i] + '" /></span>';
} else {
alert("The maximum emote(" + EmoteNameLines[i] + ") size is (36x36)");
}
}
} else {
alert("You have an unbalanced amount of emote parameters.");
}
}
The span tag's onclick calls this function:
function appendEmote(em) {
shoutdata.value += em;
}
Every time I click a button that has an onclick attribute, I get this error:
Uncaught ReferenceError: function is not defined.
Update
I tried using:
emoteTab[2].innerHTML += '<span style="cursor:pointer;" id="'+ EmoteNameLines[i] +'"><img src="' + EmoteURLLines[i] + '" /></span>';
document.getElementById(EmoteNameLines[i]).addEventListener("click", appendEmote(EmoteUsageLines[i]), false);
But I got an undefined error.
Here is the script.
I tried doing this to test if listeners work and they don't for me:
emoteTab[2].innerHTML = '<td class="trow1" width="12%" align="center"><a id="togglemenu" style="cursor: pointer;">Custom Icons</a></br><a style="cursor: pointer;" id="smilies" onclick=\'window.open("misc.php?action=smilies&popup=true&editor=clickableEditor","Smilies","scrollbars=yes, menubar=no,width=460,height=360,toolbar=no");\' original-title="">Smilies</a><br><a style="cursor: pointer;" onclick=\'window.open("shoutbox.php","Shoutbox","scrollbars=yes, menubar=no,width=825,height=449,toolbar=no");\' original-title="">Popup</a></td></br>';
document.getElementById("togglemenu").addEventListener("click", changedisplay,false);
Never use .onclick(), or similar attributes from a userscript! (It's also poor practice in a regular web page).
The reason is that userscripts operate in a sandbox ("isolated world"), and onclick operates in the target-page scope and cannot see any functions your script creates.
Always use addEventListener()Doc (or an equivalent library function, like jQuery .on()).
So instead of code like:
something.outerHTML += '<input onclick="resetEmotes()" id="btnsave" ...>'
You would use:
something.outerHTML += '<input id="btnsave" ...>'
document.getElementById ("btnsave").addEventListener ("click", resetEmotes, false);
For the loop, you can't pass data to an event listener like that See the doc. Plus every time you change innerHTML like that, you destroy the previous event listeners!
Without refactoring your code much, you can pass data with data attributes. So use code like this:
for (i = 0; i < EmoteURLLines.length; i++) {
if (checkIMG (EmoteURLLines[i])) {
localStorage.setItem ("nameEmotes", JSON.stringify (EmoteNameLines));
localStorage.setItem ("urlEmotes", JSON.stringify (EmoteURLLines));
localStorage.setItem ("usageEmotes", JSON.stringify (EmoteUsageLines));
if (i == 0) {
console.log (resetSlot ());
}
emoteTab[2].innerHTML += '<span style="cursor:pointer;" id="'
+ EmoteNameLines[i]
+ '" data-usage="' + EmoteUsageLines[i] + '">'
+ '<img src="' + EmoteURLLines[i] + '" /></span>'
;
} else {
alert ("The maximum emote (" + EmoteNameLines[i] + ") size is (36x36)");
}
}
//-- Only add events when innerHTML overwrites are done.
var targetSpans = emoteTab[2].querySelectorAll ("span[data-usage]");
for (var J in targetSpans) {
targetSpans[J].addEventListener ("click", appendEmote, false);
}
Where appendEmote is like:
function appendEmote (zEvent) {
//-- this and the parameter are special in event handlers. see the linked doc.
var emoteUsage = this.getAttribute ("data-usage");
shoutdata.value += emoteUsage;
}
WARNINGS:
Your code reuses the same id for several elements. Don't do this, it's invalid. A given ID should occur only once per page.
Every time you use .outerHTML or .innerHTML, you trash any event handlers on the affected nodes. If you use this method beware of that fact.
Make sure you are using Javascript module or not?!
if using js6 modules your html events attributes won't work.
in that case you must bring your function from global scope to module scope. Just add this to your javascript file:
window.functionName= functionName;
example:
<h1 onClick="functionName">some thing</h1>
I think you put the function in the $(document).ready.......
The functions are always provided out the $(document).ready.......
I got this resolved in angular with (click) = "someFuncionName()" in the .html file for the specific component.
Check the casing of your functions.
onclick="sillyLongFunctionName"
and
function sillylongFunctionName() { ...
Are not identical. Hard to spot sometimes!
If the function is not defined when using that function in html, such as onclick = ‘function () ', it means function is in a callback, in my case is 'DOMContentLoaded'.
See that your function is not in a callback function if you are using an external js file.
Removing the callback function would do the trick
(function() { //comment this out
//your code
})(); //comment this out

Html.CheckBox onclick event is getting escaped

I'm trying to generate a series of check boxes that will update a span to keep track of the number of check boxes that have been checked (shows something like "4 of 12 checked"). I've created the JavaScript function updateSelected that handles that, but it requires the ID of the span and the class of the check boxes to count, so I tried this code to generate each check box:
#Html.CheckBox(string.Format("contentprofilestate[{0}].selected", i),
new { onclick = "updateSelected('" + selectedSpanId + "', '" + checkboxClass + "')" })
This produces the following HTML:
<input id="contentprofilestate_6__selected" name="contentprofilestate[6].selected"
onclick="updateSelected('StMarySpan', 'StMaryCheck')"
type="checkbox" value="true" />
How can I get the onclick event handler to render without escaping the apostrophes? Alternatively, is there a better way to accomplish this task (if so, feel free to use jQuery in your suggestion)?
Note re: Lester
I created a new project and created this view and it still escaped the apostrophes:
#{
ViewBag.Title = "index";
}
<h2>index</h2>
#Html.CheckBox(string.Format("contentprofilestate[{0}].selected", 0), new { onclick = "updateSelected('" + "test" + "', '" + "test2" + "')" })
Alternate Solution
I was not able to figure out how to keep the apostrophes from being escaped, so I wrote some jQuery logic to do what I need without any inline JavaScript:
jQuery(document).ready(function () {
jQuery(':checkbox').click(function () {
var clientHeader = jQuery(this).closest('.client-header');
var clientCheckedSpan = clientHeader.find('.client-checked');
var inputChecked = clientHeader.find('input:checked');
clientCheckedSpan.text(inputChecked.length);
});
});
It's very odd that the apostrophes are being encoded. I didn't think they would be and after doing a quick test they aren't. There's probably something else going on in that view. If you place just that 1 line in an empty view I'm betting you won't get the same problem.
As for any other alternatives, you can easily get the number of selected checkboxes using jQuery:
var numChecked = $("input:checked[id^=contentprofilestate]").size();

Categories