Andres Quintero
Andres Quintero

Reputation: 253

Javascript function from external file not working

I'm new to programming and currently learning javascript. I'm trying to test what I've learned so far using a payroll calculator, but my code is not working. I created a function that will be called when the user clicks a button; if I add the function inside the same HTML file, it works fine. When the function is in my external javascript file, the function doesn't work. I clicked on View Page Source to confirm the external javascript file was loaded and it was.

HTML CODE

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Payroll Calculator</title>
    <meta name="viewport" contect="width=devide-width, user-    scalable=no,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/styles.css">
    <link rel="stylesheet" href="css/myStyle.css">
</head>

<body>
    <header>
        <div class="container">
            <div class="col-xs-12">               
                <h1>Payroll Calculator</h1>
            </div>    
        </div>
    </header>

    <div class="container">
        <section class="main row">
            <article class="col-xs-12 col-sm-6 col-md-6">
                <h3>Employee</h3>
                <div class="input-group">
                    <span class="input-group-addon" id="firstName">First Name</span>
                    <input type="text" class="form-control" placeholder="First Name" aria-describedby="basic-addon1">
                </div>
                <br>
                <div class="input-group">
                    <span class="input-group-addon" id="basic-addon1">Last Name</span>
                    <input type="text" class="form-control" placeholder="Last Name" aria-describedby="basic-addon1">
                </div>
                <br>                    
                <div class="input-group">
                    <span class="input-group-addon" id="rate-type">Rate Type&nbsp</span>
                    &nbsp<input type="radio" name="optradio">Hourly 
                    <input type="radio" name="optradio">Salary
                </div>
                <br>
                <div class="input-group">
                    <span class="input-group-addon">Rate Amount</span>
                    <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
                    <span class="input-group-addon">.00</span>
                </div>
                <br>

                <label id="EnterHours" onclick="enterHours()">Enter Hours</label>
                <br>
                <p id="hours"></p>
                <br>

                <label id="EnterEarnings">Enter Earnings</label>
                <br>
                <p id="earnings"></p>
                <br>                    

<button type="button" class="btn btn-default"    onclick="enterHours()">Calculate</button>
<button type="button" class="btn btn-default">Cancel</button>


            </article>

            <aside class="col-xs-12 col-sm-6 col-md-6">
                <h3 class="sidebar">Results</h3>
            </aside>
        </section>
    </div>

    <footer>
        <div class="container" >
            <h3>Andres Quintero</h3>
            <p id="demo"></p>
        </div>
    </footer>

<script src="js/app2.js"></script>
</body>

Javascript Code

$(document).ready(function(){

   function enterHours(){
       document.getElementById("hours").innerHTML = "This is a test";    
   }

}

Upvotes: 2

Views: 4551

Answers (2)

connexo
connexo

Reputation: 56744

Scope WTF?? What is going on?

It's about Javascript scope. If you define a function b inside another function a, b will only be known (and thus, callable) inside a, but not outside of it.

Let's have a look at your code, specifically at the argument you pass to $(document).ready() (which happens to be another function [actually even two: $() and ready()]):

function() {
   function enterHours(){
       document.getElementById("hours").innerHTML = "This is a test";    
   }
   // enterHours will be known inside the outer function
}
// enterHours will be unknown outside the outer function

The outer function is a so-called anonymous function (it does not have a name), inside which you define a named function called enterHours.

So what if you want to define a function inside the outer function that is supposed to be "callable from everywhere", which in JS lingo is in the global scope?

To make it available everywhere, you have two options. One is, you define your function outside the context of any other function:

function enterHours(){
   document.getElementById("hours").innerHTML = "This is a test";    
}

Internally, what is going to happen, is that the browser will attach this function to the so-called "global object" window. Note that this object does only exist in browsers whereas Javascript can also be run in other contexts like node.js.

If you just enter the above three lines in the browser console of your Chrome browser (accessible in Windows by pressing F12, on Mac OS X bei pressing CMD-ALT-i), you can easily check what I said by typing window.. In the completion box you will now find a property on the window object called enterHours. Any property of the window object can either be referenced by calling it explicitly on the window object (like you just did in the console typing window.) or by simple referencing it without window..

So this is the implicit way to define window.enterHours.

The other option (which you can make use of anywhere in your code) is the explicit attaching of enterHours to the global object:

window.enterHours = function () {
   document.getElementById("hours").innerHTML = "This is a test";    
}

Notice how we moved the name here. This creates a property on the window object named enterHours and assigns a reference to an anonymous function to it. That function will from now on be callable using either window.enterHours(); or simply enterHours();.

Working solution:

Having read the above explanation you will understand why the following code solves your problem:

$(document).ready(function(){
   window.enterHours = function () {
       document.getElementById("hours").innerHTML = "This is a test";    
   }
}

More on scope in general (regardless of programming language):

https://en.wikipedia.org/wiki/Scope_(computer_science)

More on Javascript scope:

http://www.w3schools.com/js/js_scope.asp

Upvotes: 1

Adam Azad
Adam Azad

Reputation: 11297

The function is defined in jQuery's scope; thus, not accessible from the global scope. Move it to global scope; do not use any wrappers

function enterHours(){
    document.getElementById("hours").innerHTML = "This is a test";    
}

Further reading How do JavaScript closures work?

Here's an example of how this works

Defining function in jQuery's scope. This will fire error:

Uncaught ReferenceError: fn is not defined

$(document).ready(function(){

   function fn(){
     console.log('Clicked!');
   }

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<button onclick="fn();">Check for fn</button>

function fn(){
   console.log('Clicked!');
}
<button onclick="fn();">Check for fn</button>

Upvotes: 3

Related Questions