83450
## やりたいこと

* * *

パスワードとパスワード確認の入力文字列をアイコンのクリックで表示非表示を切り替えたいです。  
下記のサイトを参考にして試みているのですがうまくいきません。  
[Login form with password show and hide button using
JavaScript](https://bootstrapfriendly.com/blog/login-form-with-password-show-
and-hide-button-using-javascript/)  
何がおかしいのかよくわからず、困っています。アドバイスいただければ有難いです。

## 現状

* * *

`gemfile`です

```

    source 'https://rubygems.org'
    git_source(:github) { |repo| "https://github.com/#{repo}.git" }
    
    ruby '2.7.4'
    
    gem 'active_storage_validations', '0.8.2'
    gem 'bcrypt',                     '3.1.13'
    gem 'bootsnap',                   '1.4.5', require: false
    gem 'bootstrap',                  '~> 4.6.0'
    
    gem 'faker'
    gem 'image_processing',           '1.9.3'
    gem 'jbuilder',                   '2.9.1'
    gem 'jquery-rails'
    gem 'kaminari'
    gem 'mini_magick',                '4.9.5'
    gem 'puma',                       '4.3.6'
    gem 'rails',                      '6.0.3'
    gem 'rails-i18n'
    gem 'sass-rails',                 '5.1.0'
    gem 'sprockets-rails',             '~> 3.2.2'
    gem 'turbolinks',                 '5.2.0'
    gem 'uglifier'
    gem 'webpacker',                  '~> 5.0'
    
    group :development, :test do
      gem 'byebug',                   '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
      gem 'factory_bot_rails'
      gem 'pry-byebug'
      gem 'pry-doc'
      gem 'pry-rails'
      gem 'rails-controller-testing', '1.0.4'
      gem 'rspec-rails'
      gem 'shoulda-matchers'
      gem 'spring-commands-rspec'
      gem 'sqlite3',                  '1.4.2'
    end
    
    group :development do
      gem 'better_errors'
      gem 'binding_of_caller'
      gem 'listen',                '3.1.5'
      gem 'spring',                '2.1.0'
      gem 'spring-watcher-listen', '2.0.1'
      gem 'web-console',           '4.0.1'
    end
    
    group :test do
      gem 'capybara',                 '3.28.0'
      gem 'database_cleaner'
      gem 'launchy'
      gem 'selenium-webdriver',       '3.142.4'
      gem 'webdrivers',               '4.1.2'
    end
    
```

viewの`form.html.erb`の一部です

```

            <%= f.label :password, "パスワード" %>
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text" id="basic-addon1"><i class="fas fa-lock"></i></span>
              </div>
              <%= f.password_field :password, class: 'form-control' %>
              <div class="input-group-append">
                <span class="input-group-text" onclick="password_show_hide();">
                  <i class="far fa-lightbulb" id="light_bulb"></i>
                  <i class="fas fa-lightbulb d-none" id="unlight_bulb"></i>
                </span>
              </div>
            </div>
    
    
            <%= f.label :password_confirmation, "パスワード 確認" %>
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text" id="basic-addon1"><i class="fas fa-lock"></i></span>
              </div>
              <%= f.password_field :password_confirmation, class: 'form-control' %>
              <div class="input-group-append">
                <span class="input-group-text" onclick="password_confirmation_show_hide();">
                  <i class="far fa-lightbulb" id="light_bulb2"></i>
                  <i class="fas fa-lightbulb d-none" id="unlight_bulb2"></i>
                </span>
              </div>
            </div> 
    
```

javascriptファイルです。  
app/javascript/packs/custom/password_show.js

```

    $(function password_show_hide() {
      var x = document.getElementById("user_password");
      var light_bulb = document.getElementById("light_bulb");
      var unlight_bulb = document.getElementById("unlight_bulb");
      unlight_bulb.classList.remove("d-none");
      if (x.type === "password") {
        x.type = "text";
        light_bulb.style.display = "none";
        unlight_bulb.style.display = "block";
      } else {
        x.type = "password";
        light_bulb.style.display = "block";
        unlight_bulb.style.display = "none";
      }
    });
    
    $(function password_confirmation_show_hide() {
      var y = document.getElementById("user_password_confirmation");
      var light_bulb2 = document.getElementById("light_bulb2");
      var unlight_bulb2 = document.getElementById("unlight_bulb2");
      unlight_bulb2.classList.remove("d-none");
      if (y.type === "password_confirmation") {
        y.type = "text";
        light_bulb2.style.display = "none";
        unlight_bulb2.style.display = "block";
      } else {
        y.type = "password";
        light_bulb2.style.display = "block";
        unlight_bulb2.style.display = "none";
      }
    });
    
```

`app/javascript/packs/application.js`の中身です

```

    require("@rails/ujs").start();
    require("turbolinks").start();
    require("@rails/activestorage").start();
    require("channels");
    require("jquery");
    require("@fortawesome/fontawesome-free");
    
    import "./custom/password_show.js";
    
    
```

何か足りない情報があればご連絡願います。  
よろしくお願いします。
CC BY-SA 4.0
2021-11-02T02:51:57.520
83384
2021-11-06T05:56:46.273
48234
question
javascript ruby-on-rails ruby
rails6 passwordの表示/非表示切り替えについて
ついこないだ似たような質問したんですが  
onclick= に packs で定義したメソッドを使おうとするとグローバルコンテキストにないとだめなようです

```

    globalThis.$ = $; // やってなかったら
    globalThis.password_confirmation_show_hide = password_confirmation_show_hide;
    
```

みたいなのが必要になるかも

参考：  
[Rails + webpacker における global と window と config/webpack/environment.js
の違い](https://ja.stackoverflow.com/questions/83118/)
CC BY-SA 4.0
2021-11-02T07:02:04.600
83392
2021-11-02T07:10:34.423
2021-11-02T07:10:34.423
83384
answer
JavaScriptの使い方自体が色々と間違っています。

まず、関数分を`$( ... );`で囲っていますが、 **これが何をするためのコードであるかを説明できますか？** まずはそこからです。

この`$(関数)`という書き方はjQueryの機能の一つです。jQueryは`<script
src='jqueyr.js'></script>`のようなに普通に読み込むと`jQuery`と`$`というグローバル変数が定義されます。この二つの変数は同じオブジェクトを指し示しており、それが、jQueryそのものです。jQueryは、それ自体を関数として使ったり、プロパティとして持っている`ajax`等を使ったりします。その使用方法の一つとして、関数を入れるという物があります。

`$(関数)`という書き方は`$(document).ready(関数)`と同じです。これは何を意味しているのかというと、HTMLドキュメント全体の準備が終わった後、つまり、HTMLに直接書かれた全てのDOMにアクセスできるようになった後に、引数にある関数が呼び出されます。HTMLないで`script`タグを使ってJavaScriptを読み込む、または、その部分にそのままJavaScriptを書いて、特に属性をしていなかった場合、そのJavaScriptは書かれているタイミングで実行されます。この時点では、`script`タグの後に何らかのドキュメントが続いていても、そこに書かれているタグ等は読み込まれていません。もし、その後に書かれているタグによって作成されるDOMについて操作がJavaScirptに書かれていた場合、まだ、DOMの準備ができていないため、うまく動作しなくなります。それを防ぐには、DOMに関する操作はドキュメント全体の準備が終わって、必要なDOMが全て作成された後に実行する必要があります。それを簡単に実行できるように用意されたのが、`$(関数)`という書き方なのです。

それを踏まえて`$(function password_show_hide() { ...
});`どうなるのかというと、jQueryがちゃんと動いている場合は、ドキュメント全体の準備が終わった後にこの関数の中身が実行されます。`$(function
password_confirmation_show_hide() { ...
});`も同じです。そう、これらは中身が実行されるだけです。なぜなら、`function password_show_hide() { ... }`の部分は
**関数定義** ではなく、 **関数式** だからです。決して`password_show_hide`という関数を宣言しているわけではありません。

```

    function f() {console.log("f");};
    !function g() {console.log("g");};
    f();
    g();
    
```

上のコードで、`f()`は実行出来ますが、`g()`はエラーになります。なぜなら、1行目は関数宣言なので関数`f`が宣言されていますが、2行目は関数式であるため関数`g`というものは宣下されていないのです。この`g`は何かというと、関数式の名前ではあるのですが、それがコード全体に`g`という名前で関数がありますよと宣言はされていないので、あとから`g()`とやっても呼び出せないのです。JavaScriptの`function`は同じような書き方に見えても、関数宣言となる場合と関数式になる場合があり、それによって、動作が違うと言うことがあります。これはとても注意が必要です。

必要なのは関数宣言でしょうか、関数式でしょうか？実際に参考にしたコードを見てみましょう。`funciton`の前に余計なものは付いていないはずです。実際のルールはもっと複雑なのですが、ほとんどの場合において、`function`の前に何も無ければ関数宣言、何かがあれば関数式です。ということで、`$(
... )`という余計な物は付けずに、参考にしたコードのようにそのまま書きましょう。(ちょっと待ってください、ここでは終わりでは無いです)

それでも、うまく動かないはずです。なぜなら、webpackはそれぞねのJavaScriptをmoduleとして扱って名前空間をわけることで、明示的に定義しない限りグローバル変数が定義されなからです。

実は、先程の`$( ...
)`自体もうまく動いていなかったはずです。なぜなら、jQueryがモジュールとして読み込まれた場合はグローバル変数を定義しないからです。`application.js`ではjqueryを読み込んでいるようですが、これ自体は何もしていないのと同じです。jQueryを使いたいなら次のように書く必要があります。

```

    var jQuery = require("jquery");
    var $ = jQuery;
    
```

これで`jQuery`や`$`が使えるようになるのですが、その有効範囲は`application.js`の中だけです。`password_show.js`で使いたい場合は、そのファイルに書く必要があります。

話がずれました。結局`$( ...
);`は必要なかったのでした。jQueryに関するコードは、ひとまず全部削除しておいた方がいいでしょう。余計なエラーを出さないようにするためにもです。

話を戻しますが、関数`password_show_hide`がどこで使われているのかを確認する必要があります。その使われているところで呼び出されればいいわけです。関数は`onclick`の属性値の中のコードで使われていました。このコードで使えるJavaScriptの関数はグローバル変数として定義された関数だけです。つまり、この関数もグローバル変数にする必要があります。

では、グローバル変数にするにはどうすればいいのか？というと @chico
さんの回答にあるリンク先を参照すればいいですね(といっても、そちらで回答しているのも私なのですが)。

```

    globalThis.password_show_hide = password_show_hide;
    globalThis.password_confirmation_show_hide = password_confirmation_show_hide
    
```

たぶん、これでうまくいくかと思います。
CC BY-SA 4.0
2021-11-06T05:56:46.273
83450
2021-11-06T05:56:46.273
7347
83384
answer