How does inline Javascript (in HTML) work? - javascript

I know this is bad practice. Don't write code like this if at all possible.
Of course, we'll always find ourselves in situations where a clever snippet of inline Javascript can address an issue quickly.
I am pursuing this query in the interest of fully understanding what happens (and the potential pitfalls) when something like this is written:
Click Me
As far as I can tell this is functionally the same as
<script type="text/javascript">
$(function(){ // I use jQuery in this example
document.getElementById('click_me').onclick =
function () { alert('Hi'); };
});
</script>
Click Me
Extrapolating from this it seems that the string assigned to attribute onclick is inserted within an anonymous function which is assigned to the element's click handler. Is this actually the case?
Because I'm starting to do things like this:
Display my next sibling <!-- Return false in handler so as not to scroll to top of page! -->
Which works. But I don't know how much of a hack this is. It looks suspicious because there is no apparent function that is being returned from!
You might ask, why are you doing this, Steve? Inline JS is bad practice!
Well to be quite honest I'm tired of editing three different sections of code just to modify one section of a page, especially when I'm just prototyping something to see if it will work at all. It is so much easier and sometimes even makes sense for the code specifically related to this HTML element to be defined right within the element: When I decide 2 minutes later that this was a terrible, terrible idea I can nuke the entire div (or whatever) and I don't have a bunch of mysterious JS and CSS cruft hanging around in the rest of the page, slowing down rendering ever so slightly. This is similar to the concept of locality of reference but instead of cache misses we're looking at bugs and code bloat.

You've got it nearly correct, but you haven't accounted for the this value supplied to the inline code.
Click Me
is actually closer to:
Click Me
<script type="text/javascript">
document.getElementById('click_me').addEventListener("click", function(event) {
(function(event) {
alert(this);
}).call(document.getElementById('click_me'), event);
});
</script>
Inline event handlers set this equal to the target of the event.
You can also use anonymous function in inline script
Click Me

What the browser does when you've got
<a onclick="alert('Hi');" ... >
is to set the actual value of "onclick" to something effectively like:
new Function("event", "alert('Hi');");
That is, it creates a function that expects an "event" parameter. (Well, IE doesn't; it's more like a plain simple anonymous function.)

There seems to be a lot of bad practice being thrown around Event Handler Attributes. Bad practice is not knowing and using available features where it is most appropriate. The Event Attributes are fully W3C Documented standards and there is nothing bad practice about them. It's no different than placing inline styles, which is also W3C Documented and can be useful in times. Whether you place it wrapped in script tags or not, it's gonna be interpreted the same way.
https://www.w3.org/TR/html5/webappapis.html#event-handler-idl-attributes

The best way to answer your question is to see it in action.
<a id="test" onclick="alert('test')"> test </a> ​
In the js
var test = document.getElementById('test');
console.log( test.onclick );
As you see in the console, if you're using chrome it prints an anonymous function with the event object passed in, although it's a little different in IE.
function onclick(event) {
alert('test')
}
I agree with some of your points about inline event handlers. Yes they are easy to write, but i don't agree with your point about having to change code in multiple places, if you structure your code well, you shouldn't need to do this.

It looks suspicious because there is no apparent function that is being returned from!
It is an anonymous function that has been attached to the click event of the object.
why are you doing this, Steve?
Why on earth are you doi.....Ah nevermind, as you've mentioned, it really is widely adopted bad practice :)

Try this in the console:
var div = document.createElement('div');
div.setAttribute('onclick', 'alert(event)');
div.onclick
In Chrome, it shows this:
function onclick(event) {
alert(event)
}
...and the non-standard name property of div.onclick is "onclick".
So, whether or not this is anonymous depends your definition of "anonymous." Compare with something like var foo = new Function(), where foo.name is an empty string, and foo.toString() will produce something like
function anonymous() {
}

using javascript:
here input element is used
<input type="text" id="fname" onkeyup="javascript:console.log(window.event.key)">
if you want to use multiline code use curly braces after javascript:
<input type="text" id="fname" onkeyup="javascript:{ console.log(window.event.key); alert('hello'); }">

Related

How do I set specific key for keydown event listener? [duplicate]

I am trying to use an HTML button to call a JavaScript function.
Here's the code:
<input type="button" value="Capacity Chart" onclick="CapacityChart();">
It doesn't seem to work correctly though. Is there a better way to do this?
Here is the link:http://projectpath.ideapeoplesite.com/bendel/toolscalculators.html click on the capacity tab in the bottom left section. The button should generate an alert if the values are not changed and should produce a chart if you enter values.
There are a few ways to handle events with HTML/DOM. There's no real right or wrong way but different ways are useful in different situations.
1: There's defining it in the HTML:
<input id="clickMe" type="button" value="clickme" onclick="doFunction();" />
2: There's adding it to the DOM property for the event in Javascript:
//- Using a function pointer:
document.getElementById("clickMe").onclick = doFunction;
//- Using an anonymous function:
document.getElementById("clickMe").onclick = function () { alert('hello!'); };
3: And there's attaching a function to the event handler using Javascript:
var el = document.getElementById("clickMe");
if (el.addEventListener)
el.addEventListener("click", doFunction, false);
else if (el.attachEvent)
el.attachEvent('onclick', doFunction);
Both the second and third methods allow for inline/anonymous functions and both must be declared after the element has been parsed from the document. The first method isn't valid XHTML because the onclick attribute isn't in the XHTML specification.
The 1st and 2nd methods are mutually exclusive, meaning using one (the 2nd) will override the other (the 1st). The 3rd method will allow you to attach as many functions as you like to the same event handler, even if the 1st or 2nd method has been used too.
Most likely, the problem lies somewhere in your CapacityChart() function. After visiting your link and running your script, the CapacityChart() function runs and the two popups are opened (one is closed as per the script). Where you have the following line:
CapacityWindow.document.write(s);
Try the following instead:
CapacityWindow.document.open("text/html");
CapacityWindow.document.write(s);
CapacityWindow.document.close();
EDIT
When I saw your code I thought you were writing it specifically for IE. As others have mentioned you will need to replace references to document.all with document.getElementById. However, you will still have the task of fixing the script after this so I would recommend getting it working in at least IE first as any mistakes you make changing the code to work cross browser could cause even more confusion. Once it's working in IE it will be easier to tell if it's working in other browsers whilst you're updating the code.
I would say it would be better to add the javascript in an un-obtrusive manner...
if using jQuery you could do something like:
<script>
$(document).ready(function(){
$('#MyButton').click(function(){
CapacityChart();
});
});
</script>
<input type="button" value="Capacity Chart" id="MyButton" >
Your HTML and the way you call the function from the button look correct.
The problem appears to be in the CapacityCount function. I'm getting this error in my console on Firefox 3.5: "document.all is undefined" on line 759 of bendelcorp.js.
Edit:
Looks like document.all is an IE-only thing and is a nonstandard way of accessing the DOM. If you use document.getElementById(), it should probably work. Example: document.getElementById("RUnits").value instead of document.all.Capacity.RUnits.value
This looks correct. I guess you defined your function either with a different name or in a context which isn't visible to the button. Please add some code
Just so you know, the semicolon(;) is not supposed to be there in the button when you call the function.
So it should just look like this: onclick="CapacityChart()"
then it all should work :)
One major problem you have is that you're using browser sniffing for no good reason:
if(navigator.appName == 'Netscape')
{
vesdiameter = document.forms['Volume'].elements['VesDiameter'].value;
// more stuff snipped
}
else
{
vesdiameter = eval(document.all.Volume.VesDiameter.value);
// more stuff snipped
}
I'm on Chrome, so navigator.appName won't be Netscape. Does Chrome support document.all? Maybe, but then again maybe not. And what about other browsers?
The version of the code on the Netscape branch should work on any browser right the way back to Netscape Navigator 2 from 1996, so you should probably just stick with that... except that it won't work (or isn't guaranteed to work) because you haven't specified a name attribute on the input elements, so they won't be added to the form's elements array as named elements:
<input type="text" id="VesDiameter" value="0" size="10" onKeyUp="CalcVolume();">
Either give them a name and use the elements array, or (better) use
var vesdiameter = document.getElementById("VesDiameter").value;
which will work on all modern browsers - no branching necessary. Just to be on the safe side, replace that sniffing for a browser version greater than or equal to 4 with a check for getElementById support:
if (document.getElementById) { // NB: no brackets; we're testing for existence of the method, not executing it
// do stuff...
}
You probably want to validate your input as well; something like
var vesdiameter = parseFloat(document.getElementById("VesDiameter").value);
if (isNaN(vesdiameter)) {
alert("Diameter should be numeric");
return;
}
would help.
Your code is failing on this line:
var RUnits = Math.abs(document.all.Capacity.RUnits.value);
i tried stepping though it with firebug and it fails there. that should help you figure out the problem.
you have jquery referenced. you might as well use it in all these functions. it'll clean up your code significantly.
I have an intelligent function-call-backing button code:
<br>
<p id="demo"></p><h2>Intelligent Button:</h2><i>Note: Try pressing a key after clicking.</i><br>
<button id="button" shiftKey="getElementById('button').innerHTML=('You're pressing shift, aren't you?')" onscroll="getElementById('button').innerHTML=('Don't Leave me!')" onkeydown="getElementById('button').innerHTML=('Why are you pressing keys?')" onmouseout="getElementById('button').innerHTML=('Whatever, it is gone.. maybe')" onmouseover="getElementById('button').innerHTML=('Something Is Hovering Over Me.. again')" onclick="getElementById('button').innerHTML=('I was clicked, I think')">Ahhhh</button>

onClick / onclick does not seem to be working as expected in HTML5 / JavaScript

I'm trying to do something very simple - call a function when a button is clicked. I've looked at several examples online, such as W3Schools and (I believe) I am using onclick / onClick correctly it does not seem to be functioning. I have tried several different methods - I'm really not sure what I'm doing wrong.
Method 1
HTML
<button id="buttonAdd" onclick="add()">Add</button>
JavaScript
function add() {
console.log("Test");
}
Result:
Test
When the button is clicked this flashes up in the console.log faster than I can easily see and then disappears.
Method 2
HTML
<button id="add">Add</button>
JavaScript
window.onload = function() {
document.getElementById("add").onclick = add;
}
function add() {
console.log("Test");
}
Result
Test
When the button is clicked this flashes up in the console.log faster than I can easily see and then disappears.
Method 3
HTML
<button id="add">Add</button>
JavaScript
window.onload = function() {
document.getElementById("add").onclick = add();
}
function add() {
console.log("Test");
}
Result
Test
This appears in the console log and remains there, without the button having been clicked.
Issue
I'm generally feeling confused. From what I can tell I am doing what is suggested by examples (the different methods I have tried reflect differences in examples).
Thanks.
Edit
So it seems the issue is the console.log flashing up almost faster than I can see... Does anyone have any idea why this might be? It seems like the page is refreshing itself, but I have no idea why this would be...
Answer
The button was in a form which caused the page to refresh when it was clicked.
the problem is the name of your function. it is the same as the id of the element. do a test an try writing this console.log(add). You will see it logs the DOM node and not the function.
is your button in a form ? because if so, then the form is submited and that's why the the page refreshes. can you post a jsfiddle with your test ?
Regarding Method 1:
I would need to see a bit more of your html structure to say for sure, but it sounds like in Method 1, the function isn't being declared properly in a way that is in scope. That might have to do with the names being the same, as theBrain mentioned or it might caused by some other problem.
Edit: From your response to theBrain, it sounds like you are able to get method 1 to work if you use different names. Given that, you can also prevent the page post by changing the onclick to include a return false value. Either of the following will work:
<button id="buttonAdd" onclick="add(); return false;">Add</button>
or
<button id="buttonAdd" onclick="return add();">Add</button>
coupled with the addition of return false; as the last line of your add() function's code.
Regarding Method 2:
In either case, method 2 is a better way of implementing this, so we can sort of ignore the reasons behind method 1 failing (though having distinctly different names for the function vs the button element would certainly be a good practice; personally, I preface all of my button ids with 'btn_').
The likely reason for the super-fast clearing of the console in both methods is that you do not have a type declared for the button. Different browsers do things differently in the absence of a type (see the tip on the W3Schools Button Tag), and it sounds like yours is treating this as a submit button, which means that it posts back to the page when clicked. You should be able to prevent this behavior by specifying type='button' within the attributes of the button element.
Regarding Method 3:
Finally, method 3 is providing the behavior that it is because your assignment statement is also executing a call to the add() function.
When the button is clicked this flashes up in the console.log faster than I can easily see and then disappears.
This is suspicious – console output is normally not cleared without user interaction.
Could it be that your page just gets reloaded – and therefor the console output “disappears”?
In general, you should not use this kind of “old-school” event handling any more anyway (unless it is for something really small-scale).
Have a look at popuplar JS libraries like jQuery etc. – they simplify event handling (amongst other things) at lot.
Mine was a little different, though I got help from #TheBrain's answer. Name of my javascript method was submit(), which was actually submitting my form. When I changed name of method to submitForm(), it worked.
I think earlier submit() was internally calling Javascript Form's submit() and not my javascript method.
Corrections invited.

javascript that produces functioning javascript

can you write javascript that produces/writes/etc. functioning javascript?
for example, have a link that has a function tied to it that when clicked produces a functioning javascipt snippet? The snippet could deal with a completely other elements.
For example
Link #1(has the javascript function that produces javascript) Link #2(does absolutely nothing for now)
Click on link #1(produces javascript snipped that says "when link #2 is clicked document.write('hello')"
Clicking on link #2 now produces "hello" whereas it previously did nothing. Is that possible?
Yes, you can dynamically assign event handlers described in text.
However, dynamic code generation is far more difficult than it sounds unless you're just following basic patters and replacing certain variables. Writing programs that write programs has long been a fascination of the computer industry, and this gave way to functional programming, which can be done in javascript.
Create the input/delete keys on the onClick handler for the datepicker, you can attach date information (or other data) when the input(s) are created. Now, you should look into $.delegate() for how to bind handlers to those inputs created. $.delegate can bind handlers to elements that are not created yet, so when they are created they will fire a handler. By storing date relevant information in the inputs via $.data() or data- attributes you will have context aware handlers for dealing with things.
If I understand your question correctly, you could do what you want with the code below.
Not sure why you'd want to do this, though.
can you write javascript that produces/writes/etc. functioning javascript?
You can do this the way I did it, or by using eval -- though, as many coders have pointed out, eval is evil!
<html>
<head>
<script type="text/javascript">
function initLinks(){
document.getElementById("link1").addEventListener("click", function(){
document.getElementById("link2").addEventListener("click", function(){
document.write("hello");
}, false);
}, false);
}
</script>
</head>
<body onload="initLinks()">
<a id="link1">Link 1</a>
<a id="link2">Link 2</a>
</body>
</html>

Invoking the HREF attribute of a link with javascript using javascript!

I never seen this before but you can invoke the HREF attribute of a link using javascript if the HREF contains javascript:;//code......;
On my example below click on both links. they do the same thing even though they have different javascript in the HREF.
for example:
<script type="text/javascript">
function clickme()
{
var link = document.getElementById("clickme");
eval(link.href);
}
</script>
<a id="clickme" href="javascript:alert('hello');">I will alert hello</a>
<br />
click me
I tested this on IE8, Firefox 3.6.8, Safari 5.0.1, and Chrome 6.0.472.55. Is this standardized, so I will not have to worry about this feature being deprecated in the future?
You don't have to worry about it being deprecated in the future. It's a bad idea now.
What's really happening there is this: There's a link using the javascript: protocol, which is respected by browsers. It means that everything following the javascript: is JavaScript and should be executed by the JS interpreter.
When you retrieve the link's href, you receive it as a string, e.g. "javascript:clickme()". You can use eval on strings to execute JavaScript. Now, you'd think that would fail (because of the javascript: protocol thing at the front), but it doesn't, because JavaScript has labels and that looks like a label when you treat it as JavaScript code.
So it works, but it's a bad idea. It's also disallowed (because of the eval) in the new "strict" mode of the latest version of JavaScript, ECMAScript 5th edition.
In general, when we think we need to use eval for something, it indicates that there's a problem with our code and that some refactoring is in order. The exceptions to that rule are very edgey edge cases that most of us will never run into. In this case, rather than having the href attribute contain the code that we want to execute, it should just use the code we want to execute. Your example, for instance, has a clickMe function as the only thing being used. Rather than evaling it, we should just call that function directly.
It won't be deprecated but I don't see the use of it.
If you do want to stream line this more do:
<script type="text/javascript">
function clickme()
{
clicked();
}
function clicked()
{
alert("hello");
}
</script>
<a id="clickme" href="javascript:clicked();">I will alert hello</a>
<br />
click me
Or better yet do:
Click Me
Or even better:
<span onclick="clicked();" class="MakeMeLookLIkeLInk">Click Me</a>
Anchor controls are mainly used for navigation, and as a good practice to keep it that way. if you have functionality that needs to take place, use a span/div with an onclick. You can use a button as well.
I think your question is whether the line
eval(link.href);
is valid.
The answer to that is yes, it is. There's no reason you can't evaluate code that's stored in some property, the same way you could evaluate the contents of an input box.
That said, this looks like a VERY bad way to code things. If you're not careful, you could end up in a circular loop. Additionally, code like this will be very hard to maintain.

Is it impossible to separate javascript from HTML?

To be specific, I'm talking about avoiding this type of code:
<input type='text' id='title_33' class='title'
onfocus='updateCharsLeft(33);'
onkeypress='updateCharsLeft(33);' />
Here I would like to put the onfocus and onkeypress event handles separately, i.e in a .js file. Like this:
$(document).ready(function()
{
$(".title").focus(updateCharsLeft);
$(".title").keypress(updateCharsLeft);
);
However here the problem is that the ID of the textbox needs to be passed onto the function updateCharsLeft(). It would suck to have to extract out the id from the ID of the textbox in that function, so it would actually be cleaner to just put in the event handlers within the HTML code.
Thoughts?
Can't you do this:
$(document).ready(function()
{
$(".title").focus(function() {
updateCharsLeft(this.id);
});
$(".title").keypress(function() {
updateCharsLeft(this.id);
});
);
or more neatly:
$(document).ready(function()
{
$(".title .another .someother .andAnother").focus(function() {
updateCharsLeft(this.id);
}).keypress(function() {
updateCharsLeft(this.id);
});
);
I've had to do something similar before and also wasn't happy with parsing the value out the ID attribute. The best thing I can suggest is that you use another attribute for the value you need, like the rel attribute:
<input type='text' id='title_33' class='title' rel='33' />
Or depending on how religious you are about validation, just use a custom attribute:
<input type='text' id='title_33' class='title' myval='33' />
I definitely think you should separate your JavaScript and HTML. It will be easier to find/maintain, and you can edit one place rather than 10. Don't repeat yourself.
I would look into using jQuery's live functionality. If you have 10 text input's, your adding 20 event handlers this way as opposed to 2 (one for EVERY focus handler on the page and one for EVERY keypress handler on the page).
Also re your comment to karim, I don't see why you would be duplicating yourself. It should be trivial to pass the id or transform it with a regular expression if necessary as long as your consistent in your naming convention For example, this.id.replace(/.*(\d+)$/,''). However, it would probably even be better to pass the direct DOM element reference instead, and work with that in your function.
Lastly, you seem to think that some aspect of this will suck. Maybe I'm missing the point, but can you clarify what's so difficult about mainupaliting the id or the DOM reference or whatever else that you need to do? Maybe post a longer code sample of what you're doing next.
If you really don’t want to pass this.Id then you could add a custom attribute to the input tag...
<input type='text' id='title_33' idSuffix='33' class='title'/>
You then set the event handlers like this...
$(document).ready(function()
{
$(".title").focus(function() {
updateCharsLeft(this.idSuffix);
});
$(".title").keypress(function() {
updateCharsLeft(this.idSuffix);
});
);
I personally like custom attributes. They provide a way to give Html tags custom metadata and keep it all in the markup. Html validators don’t like them though :-(
I would try to manage the all events from a single JavaScript block. As long as you are able to reference elements on the page, this should be possible.
You could always use the string functions to parse out the value and pass it along, e.g.
$(".title").focus(function() {
var thisid = this.id;
updateCharsLeft(thisid.substring(thisid.indexOf("_") + 1));
});
I'm not sure what the performance implications would be if you had to do this for a lot of elements though.
$(document).ready(function(){
var update = function(){ updateCharsLeft(this.id.split("_")[1]); };
$("input.title")
.focus(update)
.keypress(update);
});
This is exactly what Unobtrusive Javascript technique focuses on.

Categories

Resources