1

Issue:

I am trying to do a simple javascript to show a <div> if a radio button is marked as 'yes', and hide if it is marked as 'no'. The javascript works when I put it in my application template <head> file, but not when I put it in an external .js file. I checked the compiled application.js and the code shows up, so I know that my external .js is being loaded in the asset pipeline.

Error Messages

The error that I get on window load in the console is:

Uncaught TypeError: Cannot read property 'checked' of null
    at locationPicked (intake.js:3)
    at Object../app/javascript/custom/intake.js (intake.js:1)
    at __webpack_require__ (bootstrap:19)
    at Module.<anonymous> (application.js:18)
    at Module../app/javascript/packs/application.js (application.js:18)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83

The error I get when clicking the 'Yes' Button is this:

Uncaught ReferenceError: locationPicked is not defined
    at HTMLInputElement.onclick

Code

Here are the versions of ruby and the primary gems from the Gemfile:

ruby '2.6.2'

gem 'rails',        '~> 6.0.2.2'
gem 'pg',           '>= 1.1.4',  '< 2.0'
gem 'puma',         '~> 4.2.1'
gem 'sass-rails',   '~> 6.0.0'
gem 'webpacker',    '~> 4.0.7'
gem 'uglifier',     '>= 4.2.0'
gem 'coffee-rails', '~> 5.0'
gem 'turbolinks',   '~> 5.2.1'
gem 'jbuilder',     '~> 2.9.1'
gem 'bootsnap',     '>= 1.4.5',             require: false

Here is my HTML and Ruby for the radio button:

# app/views/events/intake.html.erb

<h2 style="text-decoration:underline; padding: 50px 0 25px 0;">Step 2 - Location</h2>
<div style="font-size: 1.5rem; padding: 0 0 15px 0;">
    Location Picked: 
    <%= f.radio_button :location_picked, 'yes', onClick: "locationPicked()" %>
    <%= label :location_picked_yes, 'Yes' %>
    <%= f.radio_button :location_picked, 'no', onClick: "locationPicked()" %> 
    <%= label :location_picked_no, 'No' %>
</div>
<div class="row" id="LocationSelection" style="display:none">
    <div class="col-5">
        <div class="card bg-light">
            <div class="card-header text-center"><h4>Select a Location</h4></div>
            <div class="card-body">
                <h5 class="card-title"><%= f.label :location_id, for: "location_id" %></h5>
                <%= f.select :location_id, options_for_select(@locations, :selected => @selected_loc),{class: "custom-select"}, id:"location_id", class: "custom-select" %>
            </div>
        </div>
    </div>
    <div class="col-1">
        <h4 class="text-center">OR</h4>
    </div>
    <div class="col-6">
        <div class="card bg-light">
            <div class="card-header text-center"><h4>Create a new Location</h4></div>
            <div class="card-body">
                <h5 class="card-title">Location Name</h5>
                <%= f.text_field :location_name, class: "form-control", type:"text", id:"org_name" %>
            </div>
        </div>
    </div>
</div>

The resulting HTML that the browser sees is this:

// Browser --> View Source

<h2 style="text-decoration:underline; padding: 50px 0 25px 0;">Step 2 - Location</h2>
<div style="font-size: 1.5rem; padding: 0 0 15px 0;">
    Location Picked: 
    <input onClick="locationPicked()" type="radio" value="yes" name="event[location_picked]" id="event_location_picked_yes" />
    <label for="location_picked_yes_Yes">Yes</label>
    <input onClick="locationPicked()" type="radio" value="no" name="event[location_picked]" id="event_location_picked_no" /> 
    <label for="location_picked_no_No">No</label>
</div>
<div class="row" id="LocationSelection" style="display:none">
    <div class="col-5">
        <div class="card bg-light">
            <div class="card-header text-center"><h4>Select a Location</h4></div>
            <div class="card-body">
                <h5 class="card-title"><label for="location_id">Location</label></h5>
                <select id="location_id" class="custom-select" name="event[location_id]"><option value="4">Helping Hands - Warehouse</option>
<option value="3">New City Church - College Room</option>
<option value="1">Samaritan Bags - Place Holder</option></select>
            </div>
        </div>
    </div>
    <div class="col-1">
        <h4 class="text-center">OR</h4>
    </div>
    <div class="col-6">
        <div class="card bg-light">
            <div class="card-header text-center"><h4>Create a new Location</h4></div>
            <div class="card-body">
                <h5 class="card-title">Location Name</h5>
                <input class="form-control" type="text" id="org_name" name="event[location_name]" />
            </div>
        </div>
    </div>
</div>

Here is the javascript file:

// app/javascript/custom/intake.js

window.onload = locationPicked();
function locationPicked() {
    if (document.getElementById("event_location_picked_yes").checked) {
        document.getElementById("LocationSelection").style.display = "block";
    }
    else document.getElementById("LocationSelection").style.display = "none";
}

Here is my application.js:

// app/javascript/packs/application.js

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require('jquery')

import '../stylesheets/application'
import 'bootstrap'

document.addEventListener("turbolinks:load", () => {
    $('[data-toggle="tooltip"]').tooltip()
    $('[data-toggle="popover"]').popover()

})

//  Custom Scripts
require("custom/global")
require("custom/intake")

When loading the javascript the way listed above, the errors happen. When I use the javascript like this in my application template <head> section it works:

// app/views/layouts/application.html.erb

<script type="text/javascript">
  function locationPicked() {
    if (document.getElementById("event_location_picked_yes").checked) {
        document.getElementById("LocationSelection").style.display = "";
    }
    else document.getElementById("LocationSelection").style.display = "none";
  }
</script>

Question

My javascript is very rusty. Any help here would be appreciated. Am I not calling the function correctly? I would think maybe a typo, but the code works in the <head> section, so I think I ruled that out. Thank you in advance for your assistance.

CWarrington
  • 659
  • 5
  • 12
  • what rails version you are using? – rock Sep 25 '20 at 04:31
  • Sorry, @rock, I should have added that to my question. I have updated my question to include relevant versions. My rails version is 6.0.2.2 though. – CWarrington Sep 25 '20 at 05:29
  • Try changing to `export default function locationPicked() {` then in `application.js` also change to `const locationPicked = require("custom/intake")` then call `window.onload = locationPicked()` in `application.js` and remove also that in `intake.js`. – dcangulo Sep 25 '20 at 05:33
  • @dcangulo, that doesn't seem to work. Getting same errors that things are not defined. – CWarrington Sep 25 '20 at 16:42

2 Answers2

0

try this code and remove script from app/views/layouts/application.html.erb

app/javascript/custom/intake.js

$(document).on('turbolinks:load', function() {
  function locationPicked() {
    if (document.getElementById("event_location_picked_yes").checked) {
        document.getElementById("LocationSelection").style.display = "block";
    }
    else document.getElementById("LocationSelection").style.display = "none";
  }
});
rock
  • 348
  • 4
  • 18
  • This does not work with browser error `Uncaught ReferenceError: locationPicked is not defined at HTMLInputElement.onclick` – CWarrington Sep 25 '20 at 16:41
0

I read this post because I'm was having a similar problem. And 15 minutes after writing asking if you had found a solution (after previous hours trying to figure it out), I figured it out; you need to delete the (), that is, window.onload = locationPicked;

My JS is not too good either, but in my case document.getElementById... was not being found. I got the clue from your saying the code worked when included on the html.erb page. Mine did too and I remembered that I had earlier used for problems

window.onload = init; 
function init() {
 <JavaScript code which works on html.erb>
}

While rewriting this, I went and found the explanation here: What is the difference between window.onload = init(); and window.onload = init;.

The simplest explanation from that thread (@Ernest Friedman-Hill): "The one without the parentheses runs the function when the window is through loading. The one with the parentheses runs the function right when the assignment is done, like perhaps while the HEAD of the page is being evaluated," (Searching was easy because the window.onload = init is common and kind of explains what is going on.) But did I know I needed to wait until the page was loaded to run the script? No!

As a final test I added () to the first line in my code and the page wouldn't load completely, but no Console errors. Which I now know is because the code means different things. The joys of JS!

Greg
  • 2,359
  • 5
  • 22
  • 35