Consider the following Javascript (which is probably not the best style):
var string = "There's an error here";
parent.innerHTML = '<div onclick="foo(\'+string+\')">'+string+'</div>';
Because of the single quote, the resulting HTML is not valid:
'<div onclick="foo('There's an error here')">There's an error here</div>'
One solution is to use the element's own innerHTML as the argument to foo() (since the function argument happens to be the same as the content of the element). However, this isn't a general solution.
Is it possible to deal with this issue using only an inline HTML solution? In my case, the inline HTML is much larger, and I can't easily convert it to a more 'proper' form, using DOM functions to create and append the HTML, and closures for the onclick functions.
I would say that your main issue is trying to attach event handlers using inline attributes instead of using addEventListener. All your issues will vanish if you respect good practices.
myDiv.addEventListener('click', function () {
foo("There's an error here");
});
However, if you still want to do it your way, you could simply escape the single quotes:
parent.innerHTML = '<div onclick="foo(\'' + string.replace(/'/g, "\\'") + '\');">' + string + '</div>';
Related
So basically I want to call a function with a parameter when I click a button. And the button is created using a string literal.
function showNewUserOnScreen(user) {
let parentNode = document.getElementById("listOfUser");
let childhtml = `<li> ${user.username} - ${user.email}
<button onclick = ${() => deleteUser(user.email)}> Delete </button>
</li>`;
parentNode.innerHTML = parentNode.innerHTML + childhtml;
}
The above image is the code that I have written, and now would like you to look at the below image of how the code gets its output in the browser.
This second image is what I get in the browser. Now when I click on this delete button it gives me the error as shown in the below image
Uncaught SyntaxError: Unexpected token ')' (at localstorage.html:1:2)
This doesn't work in the way you think it does (or should). Template literals will evaluate the contents of the expressions you enclose in ${}, so an expression representing a function definition won't evaluate to much of anything useful in this context.
If you're intent on using inline on* parameters and generating DOM elements from HTML strings (both no longer recommended practices in favor of using addEventListener() and createElement()/appendChild() (and other DOM manipulation methods), respectively), just include the name of the function you want to run on that event and use the template literal portion to pass just the string argument:
let childhtml = `<li> ${user.username} - ${user.email}
<button onclick="deleteUser('${user.email}')"> Delete </button>
</li>`;
This will lead to issues (including security issues) down the road if user.email can contain apostrophes (' or "); as such I would recommend switching to the more modern syntaxes I mentioned above (among other reasons).
I'm trying to add variable inside th:onsubmit with
th:onsubmit="return confirm('Hi '" + ${user.name} + "')"
but it always get me error like
Malformed markup: Attribute "+" appears more than once in element
also i can't find onsubmit example on thymeleaf official document
There is nothing special about onsubmit which is why there is nothing in the official documentation about it -- you're simply not formatting the expression correctly. I would format the expressions like this:
th:data-username="${user.name}"
onsubmit="return confirm('Hi ' + this.getAttribute('data-username'))"
(To avoid security errors, Thymeleaf may prohibit you from concatenating strings directly in your JavaScript, which is why I'm separating it out to it's own attribute.)
You can use a function in your onsubmit event, and assign the Thymeleaf expression to a variable within that function.
Example (using onclick in my case):
<yourtag ... onclick="runMyFunction();"></yourtag>
...
<script th:inline="javascript">
function runMyFunction() {
var user_name = [[${user.name}]];
console.log(user_name);
}
</script>
This uses Thymeleaf's JavaScript inlining syntax [[${...}]].
Note that in this case, the event does not have to be th:onsubmit - just onsubmit.
I have an event handler that appends a new row to a table.
$("#pre_script_12").change(function() {
$("#selected_pre_scripts")
.append("<tr><td><a href='javascript:moveScriptUp("
+ this.id
+ ")'>Move</a></td></tr>");
moveScriptUp() looks like this:
function moveScriptDown(id) {
current = $("tr." + id).eq(1);
$(current).prev().before($(current).next());
$(current).prev().before($(current).next());
}
However, I am getting an error when when appending id to tr..
Error: Syntax error, unrecognized expression: tr.[object HTMLInputElement]
How can I supply the id as a parameter to the above JavaScript function without getting this error when attempting to use it in a jQuery selector?
You need to quote strings in your function call.
Right now, your <a> tag looks like this:
<a href='javascript:moveScriptUp(pre_script_12)'>Move</a>
In (most) browsers, all elements IDs become a global variable, so pre_script_12 refers to the element. You need to add quotes, so you're passing a string:
$("#selected_pre_scripts")
.append("<tr><td><a href='javascript:moveScriptUp(\""
+ this.id
+ "\")'>Move</a></td></tr>");
P.S. I highly reccomend, not using inline JavaScript. Why not bind a (delegated) event handler to call the moveScriptUp() function?
I know this is really basic javascript but for some reason, I can't seem to get my link's onclick function to work when passing a parameter.
I have tried escaping the quotes, adding different types of quotes and adding the raw variable as a string.
I have it working with the below but it says that "XYZ is undefined"
function renderLink(value, meta, record)
{
var type = record.data['name']; //value is XYZ
return '';
}
function getReport(type){
alert(type);
}
return '';
You need to escape the string:
return '';
If you look at the rendered HTML, you'll see the problem: Your getReport call looks like this:
getReport(XYZ);
I'm guessing you want quotes around that, so:
return '';
...which renders:
getReport('XYZ');
Live example
Somewhat more esoteric, but when you output an onclick attribute as part of HTML source, it is of course an HTML attribute, which means you can use character entities. So you could use the " entity:
return '';
Live example
I point this out not because I recommend it (I don't), but because it's useful to remember what's really going on in an onclick attribute. This is one of the reasons I would strongly recommend using a proper event handler (e.g., via addEventListener / attachEvent, or even just assigning to the a element's onclick property once it's been instantiated) instead.
It's important to note that this way of doing it is also very sensitive to the content of record.data['name']. For instance, consider what happens if instead of XYZ it's Tom's. The output of the first option above would be
getReport('Tom's');
...which is obviously a problem. Similarly, if there's a backslash in the text, it will be treated as an escape character on the result, etc., etc. — a bit of a minefield.
If you can possibly change your renderLink so it returns an actual instantiated a element rather than a string, that's what I'd do:
function createLink(value, meta, record)
{
var type = record.data['name']; // Grab value as of when we were called
var link = document.createElement('a');
link.href = "javascript:void(0);";
link.onclick = function() { // Or even better, addEventListener / attachEvent
getReport(type);
return false;
};
return link;
}
That creates the link and a closure that accesses type without turning it into text and back again. (Don't worry if you're unfamiliar with closures, closures are not complicated.)
Live example
getReport receives XYZ as a variable not as a string, you need to put that inside quotes:
return '';
I have a question, which I can't seem to decide on my own so I'll ask here. The question is simple: whether to use inline JavaScript events or adding them afterwards? The theory in the background isn't that simple though:
I have a JS object that returns HTML. Whenever you create this object, the returned HTML will be used for another object's HTML. Therefore, adding events is not straight-forward. See:
secret.object = function() {
this.init = function() {
var html = '<div>and lots of other HTML content</div>';
return html;
};
}
This is a sample object that is created within this code:
for ( var i = 0; i < countObjects; i++) {
var obj = arguments[0].content[i];
generatedContent += spawnSecret(); /* The spawnSecret() is a method that initializes the object, and calls its init() method that returns the HTML.
}
and then later on I create a new object whose property "content" will be set to "generatedContent". It needs to add the events within the secret object I have, nowhere else. And since my system is built like this, I see only two ways around this: use inline events or build HTML using method calling instead of returning.
Hopefully, this wasn't too hard to understand.
If you created the elements using document.createElement() (but didn't append them to the DOM) and kept a reference to them, then you could populate them with the text content and attach event handlers to them, without having to use inline events.
When you are ready to reveal your 'secret' you could then append them to the DOM, rather than dumping in a text string of HTML tags and content.
I cant see it making much of a difference - if you just render your events using onclick etc. JavaScript event handlers they will be evaluated as soon as you append your generated HTML to the document, rather than you having to call attachEvent() or whatever.