Making scoped functions accessible from a main onload function within the <head> - javascript

An oversimplified sample of my code:
<head>
<script type="text/javascript">
main();
function main() {
if(document.readyState == 'complete') {
function a() { /*do stuff*/ }
function b() { /*do stuff*/ }
} else {
setTimeout(function() { main(); }, 1000);
}
}
</script>
</head>
<body onload="javascript: main();">
<div onclick="javascript: a();"></div>
<div onclick="javascript: b();"></div>
</body>
I am wondering if there is a way for onclick events for a() and b() to be recognised without putting the <script> tag at the end of the document. I know I could do that relatively easily by making variables and functions global, but it's hardly good practice. I could probably utilise closures, but my original code has more functions than 2, and it would be quite a pain.

I know I could do that relatively easily by making variables and functions global, but it's hardly good practice.
If you're going to use onxyz-attribute-style event handlers, you have to make those functions accessible globally.
Which is one of the many reasons not to use them.
...without putting the tag at the end of the document.
That's an odd requirement, as that's where the script tag should go.
But you can do it in a couple of ways:
Using the DOMContentLoaded event, and hooking up your handlers using addEventListener within your event handler for it.
Using a setTimeout loop to wait for the elements to appear (usually this is a backstop for environments that don't have DOMContentLoaded, if there are any left).
Using event delegation, where you hook the event on document or document.body, then check whether the event travelled through the elements you're interested by looking at event.target and its parent elements (or using the relatively-new closest method).
Barring a really good reason for keeping the script in head, your best bet here is:
<head>
</head>
<body>
<div id="divA">divA</div>
<div id="divB">divB</div>
<script type="text/javascript">
main();
function main() {
document.getElementById("divA").addEventListener("click", function a() {
console.log("a click");
});
document.getElementById("divB").addEventListener("click", function b() {
console.log("b click");
});
}
</script>
</body>
...where using ids is just an example. In practice, you can often identify the relevant elements via a CSS selector you can use with document.querySelector or document.querySelectorAll.
Side note: You don't use the javascript: pseudo-protocol on onxyz-attribute-style event handlers. (You use it where a URL is expected, such as an href or a bookmark.)

Moving the script element won't help.
The a and b functions exist in the scope of the main function and are not accessible outside it.
The onclick functions depend on a and b being globals.
Either make them globals or transform the onclick attributes into JavaScript addEventListener calls (inside the main function).

Related

function not working on click? (jquery/javascript)

This is the most frustrating error I have probably ever had while coding. I'm trying to click a button and have it pop up "hello" in an alert box. If I remove the document ready it works. If I remove the function and just have the alert box in the document.ready, it also works but obviously without a click. However, that JS doesn't work all together and I have no idea why.
HTML
<button id="signup-box-button" type="button" onclick="signupSubmit()">sign up</button>
JS
$(document).ready(function(){
function signupSubmit(){
alert("hello");
}
});
When you put a function or variable inside a function its scope is limited to that function, this means that it will be visible only when inside that function.
To solve this, just move the function that is used outside of "ready function" to be visible outside.
$(document).ready(function(){
// code here
});
function signupSubmit(){
alert("hello");
}
Or, better yet. Leave Javascript code to Javascript file only, and let the HTML with HTML only.
HTML
<button id="signup-box-button" type="button">sign up</button>
Javascript
$(document).ready(function(){
$('#signup-box-button').click(function(){
alert("hello");
}
});
This is because of scoping in javaScript as explained by Skarllot
Please refer to know how this keyword works with inline event handlers.
You can use the below code snippet
$(document).ready(function(){
this.signupSubmit = function(){
alert("hello");
}
});
Refer JSFIDDLE
By adding this keyword the function will be registered in global scope. And the above solution may pollute the global namespace.
To better understand the above functionality:
Inline event handlers are evaluated in global scope. In your first example, signupSubmit is not defined in global scope, it is defined inside the function you pass to $(document).ready. Hence the inline event handler cannot find the function.
Here is a simplified example:
function ready() {
function signupSubmit() { }
}
ready();
signupSubmit(); // ReferenceError because signupSubmit is not defined in this scope
Note that there is no reason to put function definitions inside a $(document).ready callback, except for explicitly not putting them in global scope by using this keyword as mentioned in my first example.

Restrict calling a JavaScript function to inline event handlers of DOM elements

Is there any approach or workaround to make a (global, if you wish, but not necessarily) function only available to inline event handlers of DOM elements?
JavaScript
function forInlineCallsOnly() {
// only callable from inline "on" attributes of DOM elements
};
That is, the above function should not be able to be called from other parts of the script, only from, for example:
HTML
<a onclick="forInlineCallsOnly();">Click me</a>
Could this be possible using stack tracing?
Note: this is not a question of best-practices regarding where event handlers should be defined.
Best answer I can give; and heck, it's not great.
<a data-onclick-attach="forInlineCallsOnly">
JS
eventMethodsMap: {
forInlineCallsOnly: function() {
...
}
}
Array.prototype.forEach.call(document.querySelectorAll('[data-onclick-attach]'), function(elem) {
elem.onclick = eventMethodsMap[elem.dataset.onclickAttach];
});
This is approximately how systems like Dojo do it; events can be defined in the HTML as data attributes, but they are hooked up in a more custom way. If you don't want to be onclick-specific, you could come up with a better query selector.
EDIT: You may also want to trim parentheses out of the onClickAttach attribute, in case people start treating it the same way as onclick.
Use the isElement function from: JavaScript isDOM -- How do you check if a JavaScript Object is a DOM Object?
And update your code to:
HTML:
<a onclick="forInlineCallsOnly(this);">Click me</a>
JAVASCRIPT:
function forInlineCallsOnly(obj) {
// only callable from inline "on" attributes of DOM elements
if(!isElement(obj)){
return;
}
// Put code you want to allow to run here
};

calling outside javascript function within jquery code

$(function(){
function f1(){}
function f2(){}
}
<Input type = radio Name = radiobutton Value = "something" checked=checked onClick= //here call f1()//>
I try to get access to f1 function in OnClick
code like this doesn't work:
$.function.f1()
$.function().f1()
That's because you should be doing it something like this:
$(function () {
function foo () { }
function bar () { }
// bind
$('input').on('click', foo);
});
... instead of putting an onclick= attribute on your HTML markup.
EDIT
As requested, some quick notes on why you should do the jQuery bind instead of the onclick markup thing.
You're already using jQuery, plain and simple. Use it.
There should be significant effort made to separate your HTML, CSS and JS. HTML in HTML files, JS in JS files, blah. Putting in onclick=do_backflips() in your HTML markup violates that, and will lead to nightmarish maintenance issues in the future, among other things.
DOM0 onclick= syntax is inherently 1:1. Which means that naturally, for each event of each element, you only get to attach one single event handler. That definitely sucks balls.
By defining your f1 and f2 functions inside the document.ready handler function, you're limiting their scope within that function. This means that they can't be referenced outside that scope, which means that the script interpreter won't know about f1 where your HTML markup is. You have to attach event handlers where the handlers are known, and that's inside document.ready (if that makes sense).
The point of using $( function(){ ... } ) is that whatever inside is run only after the DOM has finished loading. There's no benefit to defining functions there unless the goal is to keep the namespace clean and use the functions in the local scope.
In this case you need the functions outside the scope so just move them outside the anonymous function or bind the event to the radio button inside the scope.

How to call JavaScript function from body of HTML document on pageload-complete

I have some JavaScript (code to initialize Google Maps if you're interested) that I'm forced to include within the <body></body> tags of an html document and I would like to have one of my methods trigger on page-load complete. The catch is that I don't have access to the <body> html tag, so I can't do:
<body onload="foo()">
Is there any way to accomplish this? I realize this is a ridiculous scenario.
Depending on when the code is run, attach the handler with JavaScript:
if(window.onload) {
var _existing = window.onload;
window.onload = function() {
_existing();
foo();
};
}
else {
window.onload = foo;
}
As you seem to have no control over the page we have to be a bit more careful. Other JavaScript might already have set an event handler. To be a good citizen, we don't just overwrite the event handler, but keep a reference to it in case it exists.
However other JavaScript code could overwrite this again.
The best way would be to use the more advanced event handling methods addEventListener (W3C) and attachEvent (IE).
For more information about event handling I suggest to read the excellent articles on quirksmode.org.
If you are using jQuery you can use $(document).ready:
$(document).ready(function() {
// put all your jQuery goodness in here.
});
See this tutorial.
window.onload = foo;
function foo(){
alert("page loaded!");
}
You can programattically attach events using the DOM.
// Function to add event listener to body
function addLoadFn()
{
var body = document.getElementById("body");
body.addEventListener("load", myOnloadListener, false);
}

Mozilla Closure Example

JS BIN Attempt
Attempting to follow along with the example, but it doesn't seem to work. A little confused, as it is Mozilla.
Mozilla
As #Xaerxess mentions, you need to call the "setupButtons" function when the DOM is ready for manipulation; typically one does that by adding an event handler to the window "load" event, which happens when the page is entirely loaded (which is what the jQuery idiom $(document).ready(function(){...}); does.
Try adding this snippet to the end of your existing <script> element to accomplish that goal using plain JavaScript, no jQuery needed:
window.onload = function() { setupButtons(); };
Another typical way of doing this is to use the element.addEventListener function; the difference is that you can add multiple event callbacks this way and they won't overwrite each other:
window.addEventListener('load', function() {
setupButtons();
}, false);
You didn't call setupButtons function on page load, only defined it. If you include jQuery, add:
$(document).ready(setupButtons);
in you script tag and it'll work.

Categories