slowjet

is a part of a carburetor

Flexible BoxでGrowlみたいな通知UIを実装する

f:id:nori_17:20130301015943p:plain

↑の画像のみたいなGrowlぽい通知UIを作るときに、一筋縄ではいかなそうなところと言えば右上から下へ順に流れていってウィンドウに収まりきらなくなったら、列が変わって左へ流れていくようなレイアウトを作るところでしょうか。言葉が下手すぎたので図を見てください・・・

f:id:nori_17:20130228215409p:plain

こういうやつ。

CSS力がめっきり減ってきてるので、確かこういうやつCSSで出来たと思うんやけどなーぐらいの感じだったんですけど、これこそFlexible Boxですね。

Flexibile Boxの詳細はわざわざ説明するのもアレなので以下の仕様書とかサイトを見て把握すればいいと思います。

HTML

<div class="mod-notifications">
  <div class="mod-notifications-notif"></div>
  <div class="mod-notifications-notif"></div>
  <div class="mod-notifications-notif"></div>
  <div class="mod-notifications-notif"></div>
</div>

CSS

Chrome25で動くようにしか書いてません

.mod-notifications {
  /* ここからFlexibile Box */
  display: -webkit-flex;
  /* directionをrowにすると行並びに */
  -webkit-flex-direction: column;
  /* 右から左へ改列しつつ並べたいので wrap-reverseを指定 */
  -webkit-flex-wrap: wrap-reverse;
  /* 上詰めは flex-start */
  -webkit-justify-content: flex-start;
  /* 右詰めは flex-start */
  -webkit-align-content: flex-start;

  padding: 10px;
  /* 上下padding10pxずつを100%から引くとちょうどになるのでcalcを使いましょう */
  height: -webkit-calc(100% - 10px * 2);

  /* 通知は画面の一番手前に表示されるべき */
  position: fixed;
  top: 0;
  right: 0;
  z-index: 10000;
}

.mod-notifications-notif {
  /* 優先度とかはない */
  -webkit-flex: 1 1 auto;

  /* 幅は300pxで固定、flex-boxはheightが有効にならないので
  min-heightとmax-heightで指定する
  が、コンテンツ量が少なくてもmin-heightに従うわけではない */
  width: 300px;
  min-height: 50px;
  max-height: 50px;

  margin: 10px 0 0 10px;
  border: solid rgba(0,0,0,.3) 1px;
  border-radius: 5px;
  padding: 10px 0;
  overflow: auto;
  position: relative;
  background-image: -webkit-gradient(linear, center top, center bottom, from(#f4f6f7), to(#e1e3e5));
  box-shadow: 0px 0px 15px rgba(10,10,10,.3), 1px 1px 1px #fff inset;
}

で、まあこれだけだと通知エリアが通知がいっぱい出たときとか画面一番手前全面に広がったりして、下にあるコンテンツがクリックできなくなる。

f:id:nori_17:20130301012805p:plain

Pointer Events

まあでもそれが出来ないのは昔の話で。なるほどのPointer Events。これも解説するのもアレなんで仕様書を読んでもらうとして・・・

ここでやることを超絶ざっくりいうと、指定した要素に対するクリックとかのイベント全般を殺せる(レイヤー自体存在してないように振る舞える)。

今の例でいくと、.mod-notificationsは存在しないように振る舞いたい、でも.mod-notifications-notifは存在させたい。

Pointer Eventsを使えば超簡単

.mod-notifications {
  pointer-events: none;
}

.mod-notifications-notif {
  /* 何も指定しないと親(.mod-notifications)の
  Pointer Events を継承して none になっているので auto にする */
  pointer-events: auto;
}

コードとデモ

次はこのNotificationsで、BackboneのViewを使ったやつとかやる気があれば書きます。

Google Chromeのインスペクタのアイコンをスッキリさせる

Chromeのインスペクタは表示してるDockをブラウザの「下・右・独立ウィンドウ」と選べるんだけど、右にしたときにアイコンがでかすぎて一番よく使うConsoleが見えない。。

Macだと「Cmd+[」で前のタブ、「Cmd+]」で次のタブ移動が出来るんだけど、いやもうそういう問題じゃない。見えないの。常に見えてないと安心できないんです。

f:id:nori_17:20130220221312p:plain

とかいう話をしてたら、 id:hokaccha がユーザースタイルで変えられるんじゃないのとか言って、変えられることを知りました。

そんなわけでスッキリさせときました。いつもConsoleちゃんが見えてる安心さ。

f:id:nori_17:20130220221323p:plain

まじスッキリ。

ユーザースタイルシートの場所

/Users/[ユーザー]/Library/Application Support/Google/Chrome/Default/User StyleSheets/Custom.css

Libraryが見えてないと見えないんで、見えるようにしてください。

さっきのキャプチャの状態を再現するCustom.css

#-webkit-web-inspector #main {
  top: 36px !important;
}
#-webkit-web-inspector #toolbar {
  height: 36px !important;
}
#-webkit-web-inspector .toggleable {
  zoom: 90%;
}
#-webkit-web-inspector .toolbar-label {
  display: none;
}
/* consoleの行間ちょっとあけといた */
#-webkit-web-inspector .console-message {
  padding-top: 3px !important;
  padding-bottom: 3px !important;
}

Gitでコミットせずに現在の変更を一時的に保存して、違うブランチで作業したい

調べても全然出てこなかったこれが、聞いたら0.5秒でstashだよって答えが返ってきました・・・git branch commit 移動 とか僕のググらビリティ低すぎわろた

とりあえず保存しときたい

git stash

または

git stash save

でワークツリーを一時的に保存できる

コメントも残しときたい

git stash "message"

保存したやつを一覧で見たい

git stash list

保存したやつを復元したい

一番新しいやつを復元

git stash pop

復元するやつを指定

stash listで見て指定する

git stash pop stash@{1}

stashを全部削除

git stash clear

ところでWebStormでは git unstash で一覧から復元できて便利

$.extend()とディープコピーを理解しよう

軽めのjQuery Advent Calendar 2012 16日目

Backbone.jsでattributesにオブジェクトを入れてハマった、っていうエントリーを書こうとしたら、ハマった僕を助けてくれたほかちゃんが先にBackbone.js Advent Calendarでエントリーを書きやがった書いてくれちゃったりしちゃったので、書くことがなくなった。

まあでも結局関連してるのって$.extend()のディープコピーだけなんで、あんまりよくわかってない人向けに書いておきます。書いてて結局これは誰向けの記事なんだろうと思い始めました。。

$.extend()って何ができんの

例えば

$.extend(a, b)

とかすると、aがbのこともできるようになります
具体的に

var shino = {
  name: 'shino',
  age: 0,
  cry: function() {
    alert('wahwahwah');
  },
  ...
};

しのちゃんは生まれたばっかりなんですね。
赤ちゃんなので泣いたりしかできません。

さて、しのちゃんは1歳になりました。
1歳になったしのちゃんは成長して挨拶ができるようになりました。

var 1YearOld = {
  age: 1,
  greeting: function() {
    alert('hello!');
  },
  ...
};

$.extend(shino, 1YearOld); 

shino.greeting();
// hello!

そんな感じです。

$.extend({}, a, b) みたいなやつを見かけました。これは何してるの

じゃあたまに見かける次のコードはなんなのっていう。

var c = $.extend({}, a, b);

最初に空のオブジェクトっぽいのが入ってるやつ。

$.extend(a, b);

さっきのこれだとaを直接拡張しちゃうから、さっきの例だとしのちゃんは1歳になってしまって、0歳ではなくなる。0歳のしのちゃんは0歳なんだし、そのまま置いておきたいと。(意味不明)つまり、aはもとのaとして置いておきたいときは

var c = $.extend({}, a, b);

とします。最初の引数に空のオブジェクトを指定すると、空のオブジェクトに対してaとbで拡張するようなイメージです。

シャロー(浅い)コピーとディープ(深い)コピー

何が浅くて、何が深いのという。
オブジェクトっていうのはそのプロパティがまたオブジェクトだったりもするわけでして、しのちゃんは16歳を過ぎてバイクの免許を取りました。バイクの免許を取ったら交通ルールとか学んでバイクのエンジンかけたり運転できるようになったりします。だいたい。

var motorcycleLicense = {
  hasMotorcycleLicense: true,
  drivingMotorcycleTechnique: {
    startTheEngine: function() {},
    drive: function() {},
    ...
  },
  ...
};

$.extend(shino, motorcycleLicense);

学んで覚えることは学校によって多少違ったりするんで、教えてもらえないこともあったりします。なので、その学校で学んだことがしのちゃんにコピーされないといけないんですけど、今、上で行ったのはシャローコピーというやつで、なんとmotorcycleLicenseの2階層目以下のオブジェクトの内容、例えばdrivingMotorcycleTechnique以下の内容が変わると、コピーした先のしのちゃんの情報まで書き換わってしまいます。これはよくない。

学校の教官が変わって、押しがけ(*1)も教えてくれるようになって、drivingMotorcycleTechniqueに押しがけ(startTheEngineWithPressing)というメソッドが追加されるとします。

*1) 押しがけっていうのは、エンジンかけるときにスタータースイッチしかないようなバイクだと、バッテリーが逝ったときに、スタータースイッチが使えなくなっちゃうんで、クラッチをつないだ状態でバイクを押しながらクランク回して、強引に点火させてしまおうという、ハードなエンジンのかけ方です。

...
$.extend(shino, motorcycleLicense); // 免許取得
motorcycleLicense.drivingMotorcycleTechnique.startTheEngineWithPressing = function() {} // 後から追加した  

console.log(shino.drivingMotorcycleTechnique);
=>
{
  startTheEngine: function() {},
  startTheEngineWithPressing: function() {}, // 覚えてないのに覚えてる
  drive: function() {},
}

そんなことができれば、しのちゃんの情報は常に自分の力以外でアップデートされるので、そんなこと出来るならやってくれ。

これはよくないというか、意図していないので、そのときにこういう第2階層以下のオブジェクトまでコピーしきってしまう方法が、ディープコピーです。やり方は簡単、第一引数にtrueを渡すだけ。

$.extend(true, shino, motorcycleLicense);
motorcycleLicense.drivingMotorcycleTechnique.startTheEngineWithPressing = function() {} // 後から追加した  

console.log(shino.drivingMotorcycleTechnique);
=>
{
  startTheEngine: function() {},
  drive: function() {},
}

後から追加した押しがけは入ってません。
つまり第二階層以下にオブジェクトがある場合は、ディープコピーにしておかないと、意図していない動作になったりしますので、気をつけましょう、という、話でした。

これほかちゃんが書いたやつと同じや!

XHR2でデータをFormDataで送るときの構造の入れ子

なんしか受け取り側が

{ parent: { child1: 'value', child2: 'value' } }

みたいになってるとき

var formData = new FormData();
formData.append('parent[child1]', 'hogehoge');
formData.append('parent[child2]', 'fugafuga');

とすればOK 

関連: http://www.html5rocks.com/ja/tutorials/file/xhr2/