Web Marina

日々の業務や勉強などで得た知識をアウトプットしていきます。

【jQuery】イベントオブジェクト ー親要素で発火するイベントを特定の子孫では解除したいー

f:id:song-of-life1352607:20180113005923j:plain

こんにちは、マリンです。

今回はJSでのイベントを発火させる要素の特定です。

親要素で指定するが、特定の子孫要素では実行してほしくない。

そんな時どのように指定すればよいでしょうか?

:notとか調べてみたのですが、

子孫要素でのやり方がイマイチよくわからず。。。

そこで今回はイベントオブジェクトを使ってこれを実装しました。


もっと簡単に実装できる、

セレクタとか、便利なメソッドとか、

皆様ぜひ教えてください。




概要

  • tableの概要を記述したtrをクリックした時、詳細の列が表示されるようにする。

  • trの中には承認ボタンがある。

  • 親のtrにイベントを付けているので、子要素のボタンのクリックでもshow hideが実行されてしまう。

以上のことから、子要素のボタンだけクリックイベントを外したいわけです。

最初は:notなどを使って特定しようとしたのですが、

うまくボタンだけを取ることが出きませんでした。

そこで、イベントオブジェクトを使ってみました。


詳細

サンプルコード

まずは解説に使うサンプルコードを記載しておきます。

<form action="sample/create" method="post">
    <table class="table">
        <tr id="sample-tr">
            <td>Name: Yamada Hanako</td>
            <td>Age: 20</td>
            <td><button name="approve" type="submit" value="approve">承認</button></td>
        </tr>
        <tr id="sample-hidden-tr style="display: none">
             <td>Address: Tokyo Minato-Ku</td>
             <td>Email: sample@example.com</td>
             <td>Password: password</td>
        </tr>
    </table>
</form>

上段のtrが表示されている行。

下段のtrが非表示部分で、上段をクリックすると表示されます。

しかし、上段3つ目のカラムにあるボタンだけはクリックの対象外にします。


以下が今回のCoffeeです。

$('#sample-tr').on 'click', (e)->
    target = $(e.target)
    if target[0].nodeName == "BUTTON"
        return
    if $(@).hasClass('show')
        $(@).removeClass('show')
        $('#sample-hidden-tr').hide()
    else
        $(@).addClass('show')
        $('#sample-hidden-tr').show()
    retrun




詳細解説

HTMLは省略します。

$('#sample-tr').on 'click', (e)->
    target = $(e.target)

まずは最初の2行です。

1行目は上段trへのクリックイベントで、引数eがイベントオブジェクトです。


2行目はイベントが発生したjQueryオブジェクトを変数に入れています。

targetの中身は以下のようになっています。

target: jQuery.fn.init(1)
  0: button
    accessKey: ""
    assignedSlot: null
    attributeStyleMap: StylePropertyMap {size: 0}
    attributes: NamedNodeMap {0: type, 1: name, type: type, name: name, length: 2}
    autocapitalize: ""
    .
    .
    .
    nextElementSibling: null
    nextSibling: null
    nodeName: "BUTTON"
    nodeType: 1
    .
    .
    .




次の2行です。

if target[0].nodeName == "BUTTON"
    return

if文でイベントが発生した要素がボタンだったら何もしないで終了させています。


if文の内容と、先ほどのtargetの中身を見比べてみてください。

target: jQuery.fn.init(1)
  0: button
    accessKey: ""
    .
    .
    .
    nodeName: "BUTTON"
    .
    .
    .
target[0].nodeName == "BUTTON"

イベントオブジェクトのtarget

その中に入っているキー[0]でDOM要素を取得し、

さらにその中からnodeNameプロパティで対象となった要素名を取得します。

そして、要素名が"BUTTON"だったら何もせずreturnというわけです。


ちなみに、最初にe.targetを変数targetに入れましたが、

今回のような単純なコードでしたら入れる必要もないと思います。

if $(e.target)[0].nodeName == "BUTTON"

でいけます。

個人的に見やすかっただけです、、、


まとめ

今まで苦手意識のあったイベントオブジェクトやjQueryオブジェクトでしたが、

一度じっくり眺めてみるとなんとなく解ってきました。

やはりぱっと見よくわからない!と思っても、

テンパることなく冷静に眺めていることって必要ですね。

余談ですが、今回のことで

なぜ$()を使ってjQueryオブジェクトにするのか?

ということが今更ながら理解できました。

根本が解っていないと理解が遅くなる一方ですね。。。

まだまだ奥が深いと思うので、これを機にちゃんと勉強してみます。

【Git】ブランチ間違えてコミットしてた!?

f:id:song-of-life1352607:20180113002051p:plain

皆さん明けましておめでとうございます。

なんとか1月終わる前に更新できました(^ ^;)

本年も地味に更新してまいりますので、

どうぞよろしくお願いいたします。

さて、今回は年明け早々やらかしたブランチ切り替え忘れ、

そしてそのまま作業した挙句コミットまでしちゃった!

その時の解決方法を載せておきます。





経緯

先日、仕事開始時にブランチを切り替え忘れ、

そのままずーっと作業し、コミットももちろん複数回行い、

作業終了時に初めて「ブランチ前回のままじゃん!!」と気づきました。

ひどい失敗で、誰かの役にたつのかわかりませんが・・・

その時行った、

間違えたブランチから本来のブランチへ変更部分を移してコミット。

という解決方法を記述します。


手順

  1. 間違えたブランチをコミットする前の状態に戻す。

  2. 変更部分を一度退避する。

  3. ブランチを切り替える。

  4. 切り替えたブランチで退避した変更部分を復活。

  5. コミットする。


詳細

間違えたブランチを、コミットする前の状態に戻す。

$git log

$git reset --soft コミットのハッシュ値

まずはgit logでどこまで戻せば良いか確認します。

次にgit reset --soft コミットのハッシュ値でブランチを

コミット前の状態に戻します。

この時絶対に--softを使ってください!

このオプションで作業ディレクトリの変更内容は残したまま、

コミットだけを取り消すことができます。


ちなみに指定するコミットのハッシュ値は、

このコミットまで戻したい!というコミットのハッシュ値を指定します。

指定したコミットの次にコミットしたものまでが取り消されます。

これに限らず、どれを指定するとどこまで影響するのか?にいつも迷います。。。


変更部分を一時退避する

$git stash

このままではcheckoutしても変更部分を反映させられないので、

この変更部分は一時的に退避させます。


ちなみに、退避させるものが複数ある場合、

$git stash save "hogehoge"

のように名前をつけておくこともできます。


ブランチを切り替える

わざわざ紹介することもないと思いますが、

$git checkout ブランチ名




切り替えたブランチで退避した変更部分を復活

$git stash pop

このコマンドは、退避した部分を復活と同時に

退避リストから削除するものです。

もちろん復活と削除を別々に行うことも可能ですが、

今回は全変更部分を一括でやってしまったので。。。

あとは切り替えたブランチでコミットすれば作業終了です。


なお、git stashの使い方については、こちらで詳しく説明してくださっています。

qiita.com

解決方法の参考にさせていただいたのはこちらです。

vatscy.github.io

www.kaasan.info




まとめ

今回は焦っていたので全変更部分を一括で移して1コミットで終わらせてしましました。

本来ならコミットごとに、

状態を一つ戻す→名前をつけて退避

を繰り返し、退避終了後

ブランチ切り替え→一つずつ復元→コミット

とすべきだったと思います。

初めてのことでパニクっていたのと、子供の迎え時間が迫っていたので

テンパって一気にどーん!とやってしまいました。

反省。。。。

しかしまずは作業前に確認しましょう。。。

本年もご覧いただき有難うございました。

f:id:song-of-life1352607:20180115004800j:plain

こんにちは、マリンです。

2018年最後の投稿です。

いよいよ今年も残すところ12時間弱。

年々1年が短くなっている様に感じます〜(´༎ຶོρ༎ຶོ`)

来年は1日1日、1仕事1仕事、1コード1コード大事に、

毎日勉強していく!!!(つもりです…(≧∀≦))

では、本年も皆さま当ブログをご覧いただき、

誠にありがとうございました!

2019年も地味に更新して参る所存ですので、

どうぞよろしくお願い申し上げます。

皆さま良いお年をお迎えくださいm( )m

【Rails】Date型↔︎文字型変換

f:id:song-of-life1352607:20180314224659p:plain

こんにちは、マリンです。

最近よく日付関係を扱うことがよくあるのですが、

Date(日付)型と文字列型の変換方法をよく忘れるので、

備忘録としてここに書かせていただきます。





Date型 → 文字列型

Date型から文字列型への変換方法。

strftime

today = Date.today    # 2018-12-05  Date型の日付(変換前)
today.strftime("%Y年 %m月 %d日")    # "2018年 12月 05日"  文字列型の日付(変換後)

Date型のオブジェクトに対し、strftimeメソッドを使うことで文字列型に変換できます。

引数にフォーマットを指定することで、その指定通りの形に変換できます。

ちなみに、%Y%yにすると年の下二桁だけを出せます。




フォーマット一覧は以下の通りです。

*文字は基本的に英語表記です。

フォーマット 変換後の出力
%A 曜日の名称
%a 曜日の省略名
%B 月の名称
%b 月の省略名
%c 日付と時刻
%d 日(01-31)
%H 24時間制の時(00-23)
%I 12時間制の時(01-12)
%j 年中の通算日 (001-366)
%M 分(00-59)
%m 月を表す数字(01-12)
%p 午前または午後(AM,PM)
%S 秒(00-60 *60はうるう秒)
%U 週を表す数。最初の日曜日が第1週の始まり(00-53)
%W 週を表す数。最初の月曜日が第1週の始まり(00-53)
%w 曜日を表す数。日曜日が0(0-6)
%X 時刻
%x 日付
%Y 西暦を表す数
%y 西暦の下2桁(00-99)
%Z タイムゾーン
%% パーセント文字




文字列型 → Date型

文字列型からDate型への変換。

to_date

to_dateメソッドを使う。

str_today = "2018-12-05"    # 文字列型の日付(変換前) 
str_today.to_date    # Date型の日付(変換後)
=> Wed, 05 Dic 2018

to_dateは楽なのでよく使います。

ただ、西暦の下二桁のみには対応していません。


strptime

Date型のstrptimeを使う。

Date.strptime('2018-12-05', '%Y-%m-%d')    # (文字列型の日付, 日付を表すフォーマット)
=> Wed, 05 Dic 2018 




to_dateに比べたらちょっと複雑になりますが、

こちらは西暦下二桁のみにも対応可能です。

Date.strptime('18-12-05', '%y-%m-%d')
=> Wed, 05 Dic 2018




まとめ

日付についてはDateやらDateTimeやらいろいろあっていつも混乱します。

また取得したものがDateなのか文字列なのか?とか。

計算するときに型が違うとエラーになっちゃいますしね(汗)

日付に関する型の違いもいずれまとめられたらと思います。

【Git】git reset --hard HEADを取り消したい!!

f:id:song-of-life1352607:20180113002051p:plain

こんにちは、マリンです。

今日Gitでやらかしました。

表題の通りなのですが、それにプラスしてDBのロールバックとかも関わってきて、

「ギャーーーー!!!」

ってなったので残しておきます。





概要

やらかした内容が多すぎるので、時系列で箇条書きします。

  1. 新規でDBのテーブルを作成するのにrails g migration ~~で作ってしまった。

  2. テーブルを作った後しばらく作業を進めてからModelが無いことに気づく。(この間何度かcommitしてる)

  3. Modelだけ作れば良いところ、わざわざrake db:rollbackしようとした。

  4. だがロールバックする前に何を思ったかCommitをテーブル作る以前まで戻してしまった。

  5. しかもGitの取り消しをgit reset --hard HEAD@{n}でやってしまった。(ここが一番問題)

  6. ロールバックしようにも当然マイグレーションファイルまで消えてしまったのでできない。




やはり一番のやらかしポイントは、commitのやり直しで

git reset --hard HEAD@{n}

を使ってしまったことでしょう。

--hardオプションをつけると、ワーキングツリーまで変更されてします。

では、これに対する対処を次から書いていきます。


対処法

1、2、3まではおそらくModelファイルだけ作ってあげれば良かった話だと思うので省略します。

4、5、6について詳細と対処法を記載しようと思います。


そもそも何が起こったのか?

私が不用意に打ったgit reset --hard HEAD@{n}

これによって何が起こったのかを解説します。

飛ばしていただいて結構です。


$ git reset --hard HEAD^
  • git reset:これは任意のcommitを取り消すコマンドです。

  • --hard :resetコマンドのオプションです。コミットだけでなく、ワーキングツリーまで元の状態に書き換えます。

  • HEAD@{n} :HEADの位置をどこまで戻すかを指示しています。@{n}で遡ることn番目のcommitを表します。




問題は真ん中の--hardオプションです。

これによりワーキングツリーまで元の状態に戻ってしまったため、

テーブル作成のために書いたマイグレーションファイルまで消えてしまいました。

(今回はテーブル作成をする一つ前のコミットまで戻ったので・・・)

そしてこれをrake db:rollbackを実行する前にやってしまいました。

該当のマイグレーションファイルが無いのにロールバックしたってできるわけがありません。

なのでこの取り消し(git reset)を取り消して、マイグレーションファイルを復活させる!

それが今回の目標です。


ちなみに問題のオプションですが、

--softにを指定すればコミットだけをなかったことにしてくれます。

これにしていればアワアワすることもなかったでしょう・・・

他にもコミットとインデックスを取り消す--mixedもあります。

オプション無しのデフォルトもこれです。

こちらでより詳しく説明してくださっています。

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法




コミット取り消しの取り消し

ここからが本題です。

消してしまった事実をなかったことにして、

マイグレーションファイルを復活させます。


$ git reflog
$ git reset --hard HEAD@{n}
$ git log

こちらが一連の流れです。

git reflog

まずどこまでコミットを戻すか履歴を見ます。

コマンドがgit logでない理由は、

リセットされたコミットはgit logの履歴に出てこないからです。

git reflogは消されたコミットも履歴として見ることができます。


git reset --hard HEAD@{n}

ここで取り消しを無かったことにします。

今回は--hardオプションで大丈夫です。

消えてしまったワーキングツリーのファイルを復活させたいので。

HEAD@{n}のnにはログで確認した該当コミットが現在地から何番目にあたるかの番号を入れてください。

reflogの時に番号入りで表示されるはずなのでわかりやすいと思います!


git log

最後に念のため確認をしましょう。

この場合はこちらの履歴で大丈夫です。

以上が解説になります。

なお、実例を交えたより詳しい説明をこちらでしてくださっていますので、

ぜひご覧ください。

git reset --hardした内容を取り消す (git reset --hard, reflog, HEAD@{x}, 取り消してしまったコミットを元に戻す) - いろいろ備忘録日記




まとめ

今までadd、commit、pushくらいしかやってこなかったGitです。

正直他のことはよくわからないから触りたく無かったし、

そもそも何ができるのかも曖昧でした。

しかし、今回初めて「履歴が残っている」ということの有り難みを感じました。

そしてむやみに手元まで変えてはならない!!ということも・・・

基本は--softでやるのが安全だし、一般的な様ですね。

大変勉強になりました!

【Bootstrap/Rails】popoverのdata-contentの中身をHelperに切り出してみた。

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

今回はBootstrapのpopoverです。

popoverはtitleに表示するタイトル。data-contentに表示する内容を書きます。

でも長い内容をタグ内に書きたくないでしょ!

ってなわけでRailsのHelperにメソッド切って表示内容を切り出してみました。

注)HTML上ではベタ書きになっちゃいますけど。




概要

- 環境 -

Rails5

Bootstrap3


- 例に使うテーブル構造 -

Usersテーブル

  • id

  • name

Childrenテーブル

  • id

  • name

  • user_id




- 概要 -

Userの名前をクリックすると、

ポップオーバーでChildrenの名前が表示されるようにします。

この時data-contentの中身が複雑になるので、

Helperにメソッドを切ってそれをdata-contentに渡します。


詳細

そもそもなんでこんなことになるのか?ちょっと詳細を説明します。

まず前提として、対象のページはindex.html.erbで、

Userの名前はUser.alleachで回して表示しているとします。

さらにChildrenは複数いる可能性があります。


そこで問題となるのが、

  1. どのUserの子供であるかを特定するためにuserを渡す必要があること。

  2. Childrenはuser.childrenの配列になること。

  3. その配列から名前を取得して表示するには、ループを回す必要があること。




ポップオーバーの内容は複雑になる場合、別エリアに切り出すことも可能です。

しかし今回の場合、まず一番外側でUser.allをループしています。

切り出すにはこのループの外になるでしょう。

となると1番のどのUserかを特定するuserを渡すのが大変です。

Ajaxを駆使してコントローラに値を渡して〜・・・なんてことが予想されます。




2、3番は表示内容が複雑になる理由です。

てことでHelperにメソッドを切ってみてはどうか?となったわけです。

ちなみに子の名前は一人一人改行して表示します。

HTML上はベタ書きになってしまいますが、コードはすっきりします。


ソースコード

さて前置きが長くなりましたが、ここから本題です。

まぁやってることは真新しいことではないのですが・・・

ControllerとModelは特に特筆することはないので、コードだけで解説は省略します。

Controller

# controllers/users_controller.rb

def index
    @users = User.all
end




Model

# user.rb

has_many :children

# child.rb

belongs_to :user




View

<!-- views/users/index.html.erb -->

<% @users.each do |user| %>
    <button class="btn" data-html="true" data-toggle="popover" title="子ども" data-cotent="<%= children_popover(user) %>">
        <%= user.name %>
    </button>
<% end %>




まずbuttonタグの中にあるdata-html="true"をご覧ください。

今回のメソッド切り出しとは直接関係はありませんが、

これを入れることでポップオーバーの中で改行<br>を使うことができるようになります。




次に本題のdata-content="<%= children_popover(user) %>"です。

このerb表記の中身が、後述のHelperに切り出すメソッド名です。

引数にuserを渡していることにご注目ください。

別エリアへの切り出しではないため、大枠のループのuserをそのまま使うことができます。




Helper

# helpers/users_helper.rb

def children_popover(user)
    users_children = []
    user.children.each do |child|
        users_children << child.name
    end
    users_children.join("<br>")
end




まず最初に子どもの名前を入れる空の配列を用意します。

そしてuser.childrenのループを回し、

その中で名前を先ほどの配列に格納していきます。

最後にループの外で出来上がった配列を、

区切り文字に<br>改行を指定して返します。

これを使うために先ほどViewでdata-html="true"を指定しました。

解説は以上になります。


まとめ

今回は値の取得などにたいした時間がかからないものでしたのでこの方法を使用しました。

しかし、表示する値の取得に時間がかかるようでしたら、

やはり別エリアに表示内容を切り出し、

値の取得にはAjaxを使って行うなどの方法をとった方が良いかと思います。

私も機会がありましたら挑戦してみようと思います。

ちなみに、今回の検証でpopoverを使用するためにclass="btn"が必ず必要だったのですが、

これは仕様でしょうか?それとも私の設定ミスでしょうか?

どなたかわかる方いらっしゃいましたら是非教えてください。

よろしくお願い致します。

【jQuery】show/hide ~イベントによって表示・非表示を切り替える~

f:id:song-of-life1352607:20180113005923j:plain

こんにちは、マリンです。

今回はボタンクリックなどのイベントによって

簡単に要素の表示・非表示を切り替えようと思います。





概要

任意の要素の表示・非表示をボタンのクリックで切り替えます。

ボタンを押すと要素を非表示に、もう一度押すと表示させます。

詳細

Rails環境で行っているため、JSの記述はcoffee scriptです。

View

<!-- app/views/samples/index.html.erb -->

<div class="target-element" data-display="on">
    <p>この部分を切り替えます。</p>
</div>

<button type="button" class="btn btn-md btn-default switch-display">Switch</button>

<script>
    samples.switchDisplay();
</script>




上から順番に解説します。

<div class="target-element" data-display="on">
    <p>この部分を切り替えます。</p>
</div>

この部分が表示したり非表示にしたりする要素です。

target-elementjQueryでこの部分を特定するためのセレクタです。

data-display="on"は、現状が表示中かどうかを判定するためのものです。


<button type="button" class="btn btn-md btn-default switch-display">Switch</button>

表示・非表示の切り替えボタンです。

Bootstrapを使っているので、btnなどのクラスでスタイルを当てています。

switch-displayはイベントの対象となるセレクタです。


CoffeeScript

class SamplesController

    switchDisplay: ->
        $('.switch-display').on 'click', ()->
            if $('.targe-element').data('display') == 'on'
                $('.switch-display').data('display', 'off')
                $('.switch-display').hide()
            else
                $('.switch-display').data('display', 'on')
                $('.switch-display').show()
        return

this.samples = new SamplesController




上から順番に解説します。(class ~~については省略します)

$('.switch-display').on 'click', ()->
    if $('.targe-element').data('display') == 'on'

.switch-display(ボタン)がクリックされたら、

まず切り替え対象の要素の持つdata-displayの値が

onであるかそうでないか検証します。


$('.switch-display').data('display', 'off')
$('.switch-display').hide()

値がonの時、つまり表示状態の時には

data-displayの値をoffにし、

切り替え対象の要素をhide()で非表示にします。


else
   $('.switch-display').data('display', 'on')
   $('.switch-display').show()

値がoffので非表示状態の時には

data-displayの値をonにし、

切り替え対象の要素をshow()で表示にします。


まとめ

要点としては、

  • 切り替えの着火剤となるイベント

  • hideによる非表示

  • showによる表示

この3点です。

今回はイベントをクリックにしましたが、

hoverなどで自作のポップオーバーに使ったりなど

イベントを変えればいろいろと応用が効くと思います。