border-image プロパティについて
border-imageってなに?どういうときにつかうの?
今回はboder-imageについて書きます。
まずはboder-imageってそもそもなに?ということからなのですが、実例を見て紹介します。
スマホのサイトを作るとき、
こういったボタンをよく使うと思います。
このボタンなんですが、「戻る」以外にも「キャンセル」などにも使用したいので、background-imageで画像を設定してしまうと、言葉を入れる部分(四角部分)の長さを、自由に変えられない(入れたい言葉の長さごとのボタン画像を用意しなければいけない)ので、普通cssで記述しますよね。
でもcssだったら左側のちょぼの影が付けられなかったり、グラデーションを使いにくかったりと不便なことがあるので、
- ちょぼは画像
- 四角い部分はcss
というコーディングをしている方が多いと思います。
でも、それだったらボタンの種類を変えたい時に、
- ちょぼの部分の画像を変更する
- 四角い部分のcssの記述も変更する
という2ステップを踏まなくてはいけません。
でも、大丈夫なのです。1ステップで変更できるのです!
そうborder-imageならね。
機能そのものは
こちらのサイトに非常に良くまとまっています。
で、こちらのサイトが非常に便利にborder-imageを生成してくれます。
ローカルからファイルを上げても大丈夫なのでかなり便利!
一度こちらを使ってある程度作っておいて、細かいところはエディターで修正していくのが良いのでは。
わかりにくい値
border-image-slice: fill
9等分した時に真ん中に囲まれている四角の部分をどうするか、です。
fillに設定すると、元画像の真ん中の部分が記述されてできたボックスの真ん中に、fillに設定しないと生成されたボックスの真ん中が透過されます。
border-image-repeat
- stretch:画像がborderの領域に合うように、拡大縮小される。
- repeat:画像がタイル状に繰り返される。タイル数が整数にならないとき、余った部分を切り捨てる。
- round:画像がタイル状に繰り返される。タイル数が整数にならないとき、一番近い整数になるようにタイル状の画像が拡大縮小される。
- space:画像がタイル状に繰り返される。タイル数が整数にならないとき、余った余白スペースをそれぞれのタイル間の余白として使う。
では次に、今回使ってみて躓いたところを次で見ていきます。。
躓いたところ
幅や高さがあわない
あくまで「border-image」なので、height指定やwidth指定をしてもその外側にborderがついてしまう。。よってborder-widthを指定し、それを考慮した上でした上でその要素のheight指定やwidth指定を行わなければならないということ。
つまり
.hoge { height: 60px; width: 20px; border-width: 10px 5px; border-image: url(hoge.jpg) 10 5; }
という風に記述するとborderとその中の要素をあわせて、目に見える画像のサイズは
height: 80px; width: 30px;
になるということだ。
t32kに聞いたらどうやらbox-sizingという便利なプロパティがあるらしい。
このプロパティの値にborder-boxを指定すると、widthとheightで指定する幅と高さがボーダーボックスに対して適用されるみたい。
なので
.hoge { box-sizing: border-box; height: 60px; width: 20px; border-width: 10px 5px; border-image: url(hoge.jpg) 10 5; }
という具合に記述してあげれば、
height: 60px; width: 20px;
のボックスができる。
border-widthとborder-image-sliceの比率関係
例えば
.hoge { border-width: 10px 5px 15px 10px; border-image: url(hoge.jpg) 10 15 30 10; }
上記のようにコーディングしたとする。
すると、
上側と左側は元画像のとおり出力されるが、
右側は幅が1/3倍になり、
下側は高さが1/2倍になる。
そのため、右下は幅は1/3倍だが高さは1/2倍というおかしい状況になる。
今回僕がコーディングしたケースでは影などの関係から左右は1/2倍で、上下は1倍のままでというようにしたかったのだが、それをしてしまうと角丸の形が崩れてしまうためできなかった。
結局上下のsliceの位置を調節することでこの問題はクリアーできたが、なんらかの事情によりsliceの位置を変更できないときに、左下のブロックだけの幅高さの割合を指定したりする方法は現状では見つけれていないので知っている人いたら教えて下さい。
css spriteって使えるの?
border-imageはcss spriteに対応しているのかなのですが、現状では対応していないみたいです。
CSS Backgrounds and Borders Module Level 3
このサイトによると
backgroundとborder-imageは違うものですよ。
ということで別々に定義している。
なので、background-positionのようにborder-image-positionというプロパティがあればcss spliteのような機能も使えたかもしれない。
しかしborder-imageのプロパティの部分を見れば分かる通り、まだそういった機能は用意されていないのでborder-imageでがcss spriteは使えないようだ。
最後に
border-imageを使えばサイトの編集を行うときにかなり便利になるが、css spriteを使えないためonclickで画像変更を行うなどの時は、画像を1枚ずつ読み込ませないといけないので、表示速度が若干遅くなってしまう。
うーん、どっちが良いのか。。
ケース・バイ・ケースですねー。
gruntの基本的な機能
gruntってなに
本家のHPいわく、
Grunt is a task-based command line build tool for JavaScript projects(http://gruntjs.com/)
ということでJavaScript周りのツールらしい。
具体的に何をやっているの?
githubのドキュメンテーションを見ると基本的に使える機能は
- concat
- init
- lint
- min
などがある。
それ以外でもプラグイン的なものが配布されているので基本で満足できない人は機能の拡張をすることができるらしい。
まずはディレクトリの作り方
test ├js │├all.js │└_core │ ├sample1.js │ └sample2.js ├grunt.js └index.html
grunt.js fileの場所についてだが、
ドキュメンテーションによると、gruntが実行されたディレクトリ内でgrunt.jsというgruntfileを探して現在のディレクトリにgruntfileがなければその親フォルダを、なければその親フォルダを、、、というようにファイルが見つかるまで親ディレクトリを探し続ける。
そのため、というようにgrunt.jsをフォルダの一番上においていおくのが流行っているらしい。
grunt fileの中身
まず、例として以下のファイルを見てみる。
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ lint: { all: ['grunt.js', 'js/_core/sample1.js', 'js/_core/sample2.js', 'js/all.js'] }, jshint: { options: { browser: true } } }); // Load tasks from "grunt-sample" grunt plugin installed via Npm. grunt.loadNpmTasks('grunt-sample'); // Default task. grunt.registerTask('default', 'lint sample'); };
上から見ていくと
module.exports = function(grunt) { // gruntの記述 };
というような記述でgrunt fileを書き始めるのがgrunt fileのルールだ。
次にgruntの基本機能の記述について見ていく
grunt.initConfig({ // gruntの基本機能 //lint やconcatなど });
プラグイン
grunt.loadNpmTasks('grunt-sample');
これはgrunt-sampleというプラグインを読み込んでいる。
このプラグインを読み込むときは事前にターミナルで
$ npm install grunt-sample
のコマンドを入力し、プラグインをインストールしておく必要がある。
ただひとつ問題があって、プラグインをインストールする先だが、grunt.jsがあるディレクトリにインストールしなければいけないらしい。なので、毎回新しく作業をはじめるごとにそのディレクトリでそのプラグインをインストールしないと。。。めんどくさい。。
実行
今回の場合
grunt.initConfig({ lint: { all: ['grunt.js', 'js/_core/sample1.js', 'js/_core/sample2.js', 'js/all.js'] }, jshint: { options: { browser: true } } });
gruntfileが上記のようになっているため、lintとjshintというタスクを実行することができる。
今回、lintだけを実行する
ターミナルを開いて
以下のコマンドを打つだけ!
$ grunt lint
これはgrunt fileでlintというタスクを設定しているので、それを実行しますよということだ。
lintの結果エラー箇所がないと以下のようなログが出力される。
Running "lint:all" (lint) task Lint free. Done, without errors.
エラーがあると以下のようログが出てしまう。
Running "lint:all" (lint) task Linting sample1.js...ERROR [L34:C2] Missing semicolon. } <WARN> Task "lint:all" failed. Use --force to continue. </WARN> Aborted due to warnings.
これはsample1.jsの34行目の2列でセミコロンを忘れているので失敗しました
ということを表している。ので、エラー箇所を直して再度実行
すると、
grunt.js js/_core/sample1.js js/_core/sample2.js js/all.js
がlintされる。
grunt.initConfigの基本的な機能
// Project configuration. grunt.initConfig({ // メタデータを記述 meta: {}, // ファイル結合 concat: {}, // JSHintを用いてlintを実行 lint: {}, // UglifyJSを用いてminifyを実行 min: {}, // QUnitを用いてtestを実行 qunit: {}, // "server" として振る舞う server: {}, // Nodeunitを用いてtestを実行 test: {}, // 監視タスクを実行 watch: {}, // JSHintのグローバル設定オプション jshint: {}, // UglifyJSのグローバル設定オプション uglify: {} });
これらを使ってgruntfileを構成していく。
この中で自分が使いそうなtaskは
- watch
- concat
- min
くらいかな。。。
なんかwatchがすごく便利そうなので少し詳しく見ていく。
watchタスク
以下のようなgrunt.jsを記述する。
grunt.initConfig({ concat: { dist: { src: ['js/_core/sample1.js', 'js/_core/sample1.js'], dest: 'js/all.js' } } min: { dist: { src: ['js/all.js'], dest: 'js/all.min.js' } watch: { files: [ 'js/_core/*.js' ], tasks: 'concat min' }, });
このファイルに対してターミナルで、
$ grunt watch
を実行すると、js/_coreのディレクトリにあるjsファイルが変更された時にconcat,minタスクを実行するというものだ。
つまり、jsファイルにに変更を加えるとそれらがall.jsというファイルにまとめられて,minifyされたall_min.jsというファイルが生成されるのだ。
なので、とりあえず作業をするときはこのへんの設定を先に行なってしまうと、作業がサクサクとすすめられそう。
what is OOCSS
object-oriented CSSについてのさわり。
いろいろなサイトを読んだ結果、OOCSSとは簡単に言うと
オブジェクト指向設計に基づいたCSSで保守性と拡張性を得よう!
ということだと解釈した。
そのための簡単な手法として例を挙げると
- なるべく子孫セレクタなどを使うのはやめて、クラス指定で書きましょう。
- クラス指定も最小限のクラスを複数書くことで、cssに同じ事を何度も書かなくて良いようにしましょう。
ということだ。
具体例として
.foo .bar + .bar { ~~~~} div:nth-child(n) {~~~~} el > el:first-child {~~~~}
上記のようにセレクタを記述すると書いた本人は良いが、他の人には伝わらない。
そこで、何を指定しているかがはっきりと分かるように明示的にセレクタを記述しなければいけない。
☓ header ul {~~~~} ◯ .site-nav {~~~~}
クラス名は、そのクラスが行う役割などを考えながらreasonableに命名する。
内容などを書いてしまっては、拡張性が失われてしまうのでやめておいたほうが良い。
では以下で、具体的にOOCSSについて書いていく。
Whta is CSS Object?
OOCSSには2つの大原則がある。
structure とskin とを独立させる。
- structure: margin , topなど、要素の場所などに関係すること
- skin: background , borderなど、要素の見た目に関すること
この2つを独立させることにより、最小限のコードでより多くの整備されたHTMLを得ることができる。
HTMLのセマンティックさ頼るよりも,structure, skinにオブジェクトやコンポーネントの名前をclassとして使ったほうが良い。
例えばmedia ojectの場合
<media class="media">
とする、その中のコンポーネントであるimgにはわざわざ
<img class="img">
をつける。
body やtext component なら
<body class="bd">
という風にする。
理由としては、img element ではなくimg クラスを参照することで、
もしここ数年で、elementが変化してしまった場合でも、cssに手を加えることなくhtmlを変更することができるからである。
セマンティックに書こう
わざわざコンテンツに関連させたようなクラス名は要らない
html5になってからタグや属性にある程度の意味を付けられるようになった。
そのため、クラス名がコンテンツをそのまま反映するのではなく、コンテンツ同士の関係を明確に表すようなクラス名をつけるのが良いだろう。
クラスの具体的な付け方
単一クラスパターン
.btn, .btn-primary { /* button template styles */ } .btn-primary { /* styles specific to save button */ }
<button class="btn">Default</button> <button class="btn-primary">Login</button>
マルチクラスパターン
.btn { /* button template styles */ } .btn-primary { /* styles specific to primary button */ }
<button class="btn">Default</button> <button class="btn btn-primary">Login</button>
この2つを見比べると
単一クラスパターンは.btm-primaryの中に.btmが含まれている(類似している)。
一方、マルチクラスパターンは.btmと.btm-primaryが独立している(全くの別物と考えることができる)。
そのため、拡張性という面から見ると、マルチクラスパターンを利用したほうが使用するトータルクラス数では圧倒的に少ないので、マルチクラスパターンを使用したほうが良いだろう。
OOCSSを使用するメリット
ここまでOOCSSを推してきたわけだが、ここで一度、OOCSSを使用した時のメリットについて見なおしてみる。
メリット
- cssがより簡単になる
- 変更を加えたい時、1箇所を変更しただけで全体のデザインを変えることができる。+
- 変更を加えるとき、1クラス1機能なので、安全に変更を加えることができる。
- 多くのクラスで定義されている機能を組み合わせることができる。
OOCSSを考えずにコーディングをすると以下のようになる。
<div id=header> </div> <div id=content> </div> <div id=sub-content> </div> <div id=footer> </div>
#header{ padding:20px; margin-bottom:20px; background-color:#121416; color:#fff; } #content{ width:640px; float:left; margin-right:20px; padding:20px; margin-bottom:20px; } #sub-content{ width:280px; float:left; padding:20px; margin-bottom:20px; } #footer{ padding:20px; margin-bottom:20px; background-color:#e4e4e4; color:#333; }
ここでOOCSSを意識しながらコーディングすると
<div class="island header"> </div> <div class="island content"> <h2>Buy now with promo code <span class=promo>0MG4WE50ME</span></h2> </div> <div class="island sub-content"> <a href=product class="island promo">Buy now!</a> </div> <div class="island footer"> </div>
.island{ display:block; padding:20px; margin-bottom:20px; } .promo{ background-color:#09f; color:#fff; tex-shadow:0 0 1px rgba(0,0,0,0.25); border-radius:4px; } .header{ background-color:#121416; color:#fff; } .content{ width:640px; float:left; margin-right:20px; } .sub-content{ width:280px; float:left; } .footer{ background-color:#e4e4e4; color:#333; }
上記のようになる。
OOCSSを意識していない方は、コンテンツを増やそうとすると、そのdiv要素にidをつけて、そのid専用のcssを書いてあげないといけない。
しかし、一方OOCSSを意識しながらコーディングした方は、新しいコンテンツに適しているクラスを幾つか選び、つけてあげるだけで拡張することができる。
OOCSS+Sass
プレーンなcssでモジュールを作るための唯一の方法は非セマンティックにクラスを定義することだ。
しかし、それには2つの問題が生じる。
- スタイルを変更するときに毎回htmlを変更したくない。(スタイルは常に変更されるものであるにも関わらず)
- クラスを追加したいDOM要素にアクセス出来ない。もし、ページ内にjsのコンポーネントを使用していても、変な記述をしない限り、その要素内にクラスを追加することはできない。
プレーンなモジュールを繰り返し使い抽象化することが、大規模なプロジェクトで保守性を維持する唯一の方法である。では、上記のような欠点なしに、メリットを得るにはどうすると良いか。
OOSass
OOCSSとSassを組み合わせるのが正解。
@extendと@mixinを使うことで、同じような記述を何度も繰り返さなくても、別のセレクタでもクラスを継承することができる。
しかし、コードを入れ子にすることで、コード量が増えてしまう。
しかし、sass3.2でプレースホルダーという機能が追加されたことによって、この問題は解決された。
以下に例を示す。
Sass
%separator border-top: 1px solid black hr @extend %separator .separator @extend %separator
hr, .separator { border-top: 1px solid black }
こういった機能によってコードの肥大化が防がれる。
実際に使われそうなケース
仮に.mediaというモジュールを、.status .profileなどに継承したいとする。
これまでだったら、.status .profileなどにも.mediaに書いたものと同じ記述をしなければいけなかったが、
sassをつかうと
%media overflow: hidden &:first-child float: left &:last-child overflow: hidden .status @extend %media // Status-specific styles here... .profile @extend %media // Profile-specific styles here...
と、記述することができる。
これによって、記述量もなり.status .profileもセマンティックなクラスになったといえる。
最後に
OOCSSとはセマンティックに、わかりやすく書くためのものである。
そのため、クラスの命名規則が重要になる。
自分が考えだしたクラス名より、ある程度すでに使われているクラス名のほうが
自分以外がコードを見た時に理解しやすいということで、以下のサイトを参考にすると良さげだった。
http://css-happylife.com/archives/2007/0115_1345.php
参考サイト
http://csswizardry.com/2012/10/a-classless-class-on-using-more-classes-in-your-html/
http://ianstormtaylor.com/oocss-plus-sass-is-the-best-way-to-css/
http://enja.studiomohawk.com/2012/03/20/about-html-semantics-and-front-end-architecture/
https://github.com/stubbornella/oocss/wiki
CSS LINT でのHタグの複数回使用エラーについて
こんにちわ。
今日は以前なんでや。。って思ったcss lint のhタグエラーについて書きます。
h2 { font-size:20px; } section h2 { font-size:18px; }
というコードがあったとしよう。
このコードをcsslintにかけると
上記のようなエラーがでる。
なぜだって思って調べてみると、
html5になる前はhタグの数字でアウトラインを決めていたから、
同じ階層に位置しているはずのh2同士のスタイルが違ったらダメでしょ!
っていう雰囲気だったらしいんですけど
html5になってsectionとかでアウトラインを明示的に示すことができるようになったから、今となってはこれも使ってもいいんじゃないかなーという人もいるみたいです。
これはfont-sizeなので
いや、h3使えよ。。。
みたいな方もいるかもしれないのですが、colorの時とか結構悩んだりしちゃいます。
なんか外国の方のサイトしか余り見かけることができなかったのですが、外国の方は結構このエラーは無視しちゃってるみたいな書き方でした。しらんけど。
結論
html5でコーディングするならcsslintでこのエラーでてもいいんじゃない?
ajax and json and jsonp
はじめまして。
mk front-end-engineerの田中塁です。
twitter API とjquery を使って、tweet情報をjson形式で受け取って自前のjsファイルで加工して表示するという事をしていたが、とあることからjsonで受け取ったファイルをローカルで保存して、そこを参照してjsonを取得するという風に書き換えなければいけないことになった。
そこで、ちょっと詰まったところがあったので備忘録的なことを含め書いておく。
ちなみにtwitter APIとのajax通信の部分は下のコード
$.ajax({ type: 'get', url: 'http://search.twitter.com/search.json?q=from%3Akeio_news&callback=?', dataType: 'jsonp', success: function (obj) { twitterTl(obj); }, error: function () { alert('通信が失敗しました'); } });
@keio_newsからtweet情報を取得するというものだ。
上のようなjson形式のデータが返ってくる。
このjsonファイルをsearch.jsonという名前で保存して
コードのurl,dataTypeの部分を以下のように書き換えた。
url: '../js/search.json', dataType: 'jsonp',
だが、これでは動作してくれなかった。
url: '../js/search.json', dataType: 'json',
という風に記述しなくてはいけなかった。
ajax通信でwebAPI等クロスドメインからjsonを参照する時はdataTypeを'jsonp'、ローカルに保存してあるjsonを参照するときは'json'とするみたいだ。。。
なぜだ。。。
両方jsonpではダメか。
webAPIの時 OK
ローカルの時 ダメ
理由
jsonpではファイルの形式は、通常
(callback関数名:例callback) ( { jsonデータ });
というようにcallback関数の引数としてjsonが与えられる。
しかし、callback=?としてjsonpデータ取得してみたところ、
jQuery182029029840137809515_1350971301001( { completed_in: 0.032, max_id: 260592894669381630, max_id_str: "260592894669381633", ....... ...... .. });
というように呼び出される関数名が「jQuery適当な数字」になってしまう。
そのため、ローカルにjsonp形式でファイルを保存しようとしてもcallbackとして呼び出される関数名がわからないため保存のしようが無いのでjsonpとしては使えないのである。
hogehoge( { jsonデータ });
上のようにjsonpファイルを書いて、
url: '../js/search.json?callback=hogehoge', dataType: 'jsonp',
という風にしてもダメだった。これは元データがjsonのためcallback関数名は指定できないからだとおもわれます。
両方jsonではダメか
ローカルの時 OK
webAPIの時 ダメ
理由
そもそも、クロスドメインのセキュリティの関係上、他のドメインにあるjsを読み込めない。
だからjson with paddingというような名前の通りjsonをjsに挟み込むようなjsonpの利用が推奨されている。
結論
ローカルではjson,webAPIではjsonpを使いましょう。