風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

TABLE の TD 要素内全てをチェックボックス化したいが……

HTMLのチェックボックス要素(input[type="checkbox"])で表示される□は、閲覧環境によっては小さく表示されてしまい、チェックを入れたり外したりしにくい場合がある。

その場合の対処法として、

  1. チェックボックスそのものをCSSで拡大する(参考:チェックボックスやラジオボタンを大きくする方法: 小粋空間チェックボックスを大きくしたい - Qiita
  2. LABEL 要素を用いて、有効な範囲を拡大する
  3. チェックボックスの代替として、画像+JavaScript 等でなんとかする

といった方法があると思われる。

で、2. の手法でやろうとしたのだが、条件を

  • チェックボックスを、TABLE 内の TD 要素下に置く
  • TD の高さや横幅は不定
  • TD 内部全体を有効範囲としたい
  • チェックボックスそのものは、TD要素の上下左右中央に表示

のように定めたところ、どうやって CSS を書けばよいのかわからなくて、悩んでしまった。




    td {
        position: relative;
    }
    
    label {
        display: block;
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background: lime;
    }
    
    input[type="checkbox"] {
        display: block; /* 無くてもよい模様 */
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        margin: auto;
    }

どうにも、冗長な書き方をしている気がしてならない。

もっとシンプルな記述方法を知りたい……ご存知の方はご教示いただきたい。

【覚書】 z-index により手前に来ている透明要素が原因でイベントが発火しないときには、 pointer-events をうまく使う

Twitter 原寸びゅーで、画像クリック時に原寸画像を開くようにしようとして、ギャラリー表示時にはうまくクリックイベントが発火しないことに気が付いた。

原因は(z-index 指定により)画像の手前に透明な要素が存在したため*1
pointer-eventsの設定を行うことで対処できそうということが分かったので、メモ書き。
なお、pointer-events は IE では IE11 以上でサポート(IE10 以下は未サポート)となる。

サンプル




(A)




(B)




(C)


画像(BUTTON)や枠の部分をクリックしたときの動作に違いがある。

覚え書き

目的として、透明要素(枠の部分・"z-index: 1"が指定されている)のクリックイベントは残しつつ、画像をクリックしたときには別のイベントを発火させたい。

  1. 最初は、単に画像にクリックイベントを設定するだけにしていた(A)。
    ところが、実際には手前に来る透明要素(枠)のイベントしか発火しない(なお、サンプルでは、画像(BUTTON)の CSS に "cursor: pointer" を設定してあるが、これも効いていないのが判る)。
  2. 手前に来る要素の CSS に "pointer-events: none" を設定(B)。
    これで、画像をクリックしたときのイベントは発火するようになったが、逆に手前の透明要素(枠)のイベントが発火しなくなってしまう。
  3. 画像を含む要素の z-index 値を、透明要素(枠)のものより大きくし、かつ、CSS に "pointer-events: none" を設定。また、このままだと、画像のイベントも発火しなくなるため、画像の方には "pointer-events: auto" を設定(C)。
    これで、画像と枠それぞれのイベントが発火するようになった。
HTMLソースコード
<div>

    <div style="width: 120px; height: 120px; position: relative; float: left; margin: 5px; background: pink;">
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 )">
            <button style="display: block; width: 50%; height: 50%; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; font-size: 12px; cursor: pointer;" onclick="alert( '画像をクリック' )">画像</button>
        </div>
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 ); z-index: 1;" onclick="alert( '枠をクリック')">(A)</div>
    </div>

    <div style="width: 120px; height: 120px; position: relative; float: left; margin: 5px; background: limegreen;">
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 )">
            <button style="display: block; width: 50%; height: 50%; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; font-size: 12px; cursor: pointer;" onclick="alert( '画像をクリック' )">画像</button>
        </div>
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 ); z-index: 1; pointer-events: none;" onclick="alert( '枠をクリック')">(B)</div>
    </div>

    <div style="width: 120px; height: 120px; position: relative; float: left; margin: 5px; background: lightblue;">
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 ); z-index: 2; pointer-events: none;">
            <button style="display: block; width: 50%; height: 50%; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; font-size: 12px; cursor: pointer; pointer-events: auto;" onclick="alert( '画像をクリック' )">画像</button>
        </div>
        <div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba( 0, 0, 0, 0 ); z-index: 1;" onclick="alert( '枠をクリック')">(C)</div>
    </div>

    <br style="clear:both;">
</div>

*1:前後移動用のナビで、こちらもクリックイベントが設定されている

Twitterで、マウスカーソル下のツイートを選択するユーザースクリプト

PCブラウザ版Twitterで、タイムラインを最初はマウスホイールで見ていて、途中からキー操作での移動に変えたいと思った場合、単に[j]キーとか押してしまうと、タイムラインの先頭(一番上・最新のツイート)まで戻されてしまう*1

そこで、現在位置(マウスカーソルのある位置)のツイートを起点に、移動開始出来たら便利かな、と思ったので、そのようなユーザースクリプトを作成してみた。
2016/03/21現在、Google Chrome(Tampermonkey)・Firefox(Greasemonkey)・Opera(Tampermonkey)の最新版にて動作確認済み。

インストール

Firefox+Gresemonkey、Google Chrome+Tampermonkey、あるいは Opera+Tampermonkeyの環境で、

twChangeSelection.user.js

furyutei/twChangeSelection: Twitterで、マウスカーソル下のツイートを選択するユーザースクリプト

をクリックし、指示に従ってインストール。

使い方

タイムライン上の目的のツイートをクリックしてポップアップ表示後に[Esc]で閉じるか、もしくは、[Ctrl]キーを押しながら目的のツイート上にマウスカーソルを動かすと、当該ツイートに(キー操作用の)カーソルが移る(ツイートの枠線に色が付く)ので、あとはそこを起点として [j]キー/[k]キー 等で移動ができるようになる。

ちなみに、自分では
furyu.hatenablog.com

と併用して、タイムライン上に見たい画像が並んでいたら、そこにカーソルを移すのに使っていたりする。

独り言など

PCブラウザ版Twitterは、ショートカットキーに対応はしているものの([?]キーを押すと、一覧が表示される)、そもそもあまり需要がないためなのか、結構難があったり、対応がおざなりだったりする気がする。

  • ↑みたいな小細工しなくても、標準の機能で起点を選択できるようになっていて欲しい。
  • 「開いたすべてのツイートを閉じる」([c]キー)は、ツイートがポップアップ表示になってから、意味がなくなっているのではないか?
  • ポップアップ表示時に、[j]/[k]による移動がうまくいかない(返信をうまく辿れない等)。
  • 非表示になっているツイート(プロモーション等)の上にもカーソルが移動してしまう&その後ときどきうまく移動できなくなる。

*1:2016/03/21現在

一般キヨシ関数

完全に乗り遅れてしまい、いまさらそのまま書いても……と思ったけれど、より一般的な問題にしてみたらどうか、と思ってやってみた。

キヨシ関数(ズンドコキヨシ、#kiyoshi関数)とは?

qiita.com

一般キヨシ関数とは?

Object general_kiyoshi( Array phrase_pattern, String last_phrase )
第一引数
phrase_pattern
文字列の配列
例:[ "ズン", "ズン", "ズン", "ズン", "ドコ" ]
第二引数
last_phrase
最後に表示出力する文字列
例:"キ・ヨ・シ!"
戻り値 無し(void:関数内部で表示も行う場合)、もしくは、出力結果の配列、あるいは、Iterableなオブジェクト(ジェネレータ)
  1. phrase_pattern に含まれる文字列を、ランダムに出力し続ける。
    文字列の出現確率は等しくなるようにする。重複する文字列がある場合、それらは同一とみなす(上記の例の場合、"ズン"が複数個含まれるが、これらは同一とみなし、"ズン"と"ドコ"が同じ確率で出現するようにする)。
  2. phrase_pattern で指定したパターンが現れたら、last_phrase を出力し、終了する。

実装例

Python

GitHub - furyutei/general_kiyoshi: general_kiyoshi(): 一般キヨシ関数

import random
import collections

def general_kiyoshi( phrase_pattern, last_phrase ):
  phrase_pattern = collections.deque( phrase_pattern )
  phrases = list( set( phrase_pattern ) )
  current_phrase_pattern = collections.deque( [], len( phrase_pattern ) )
  
  def random_phrase_generator():
    if len( phrases ) <= 0:
      return
    
    while True:
      yield random.choice( phrases )
  
  def check_phrase_pattern():
    return ( current_phrase_pattern == phrase_pattern )
  
  for phrase in random_phrase_generator():
    yield phrase
    
    current_phrase_pattern.append( phrase )
    
    if check_phrase_pattern():
      break
  
  yield last_phrase


def kiyoshi():
  for phrase in general_kiyoshi( [ 'ズン', 'ズン', 'ズン', 'ズン', 'ドコ' ], 'キ・ヨ・シ!' ):
    print( phrase )

def cockrobin():
  for phrase in general_kiyoshi( [ 'パパンがパン', '誰が殺した', 'クックロビン', '誰が殺した', 'クックロビン' ], 'スズメが弓と矢でもって、クックロビンを殺したの' ):
    print( phrase )


kiyoshi()
# :
#ズン
#ズン
#ドコ
#ズン
#ズン
#ズン
#ズン
#ドコ
#キ・ヨ・シ!

cockrobin()
# :
#パパンがパン
#クックロビン
#誰が殺した
#誰が殺した
#誰が殺した
#誰が殺した
#パパンがパン
#パパンがパン
#クックロビン
#誰が殺した
#誰が殺した
#パパンがパン
#誰が殺した
#クックロビン
#誰が殺した
#クックロビン
#スズメが弓と矢でもって、クックロビンを殺したの
JavaScript
'use strict';

function * general_kiyoshi( phrase_pattern, last_phrase ) {
    var phrases = phrase_pattern.filter( function ( value, index, self ) {
            return ( self.indexOf( value ) === index );
        } ),
        current_phrase_pattern = new Array( phrase_pattern.length );
    
    function * random_phrase_generator() {
        if ( phrases.length <= 0 ) {
            return;
        }
        for (;;) {
            yield phrases[ Math.floor( Math.random() * phrases.length ) ];
        }
    }
    
    function check_phrase_pattern() {
        return phrase_pattern.every( function ( value, index, self ) {
            return ( current_phrase_pattern[ index ] === value );
        } );
    }
    
    for ( var phrase of random_phrase_generator() ) {
        yield phrase;
        
        current_phrase_pattern.shift();
        current_phrase_pattern.push( phrase );
        
        if ( check_phrase_pattern() ) {
            break;
        }
    }
    
    yield last_phrase;
}

function print() {
    console.log.apply( console, arguments );
}

function kiyoshi() {
    for ( var phrase of general_kiyoshi( [ 'ズン', 'ズン', 'ズン', 'ズン', 'ドコ' ], 'キ・ヨ・シ!' ) ) {
        print( phrase );
    }
}

function cockrobin() {
    for ( var phrase of general_kiyoshi( [ 'パパンがパン', '誰が殺した', 'クックロビン', '誰が殺した', 'クックロビン' ], 'スズメが弓と矢でもって、クックロビンを殺したの' ) ) {
        print( phrase );
    }
}

kiyoshi();
cockrobin();

独り言など

【覚書】 Twitter の画像サイズの変換など(PNG・JPEG)

Twitter に画像(PNG・JPEG)をアップロードし、これを表示する際、URLの末尾によってそれぞれ異なるサイズの画像が得られるが、これに関する覚え書きなど。



Twitter の画像サイズ種別

Twitter にアップロードした画像は、以下のような URL で参照(表示)できる。

https://pbs.twimg.com/media/~.<拡張子>[:サイズ種別]

サイズ種別は以下のようになっている。

:サイズ種別 最大サイズ(*1)
:thumb 150px×150px
:small 680px×680px 横340px(*2)
680px×680px(*3)
:medium
(なし)
1200px×1200px 横600px(*2)
1200px×1200px(*3)
:large 2048px×2048px 横1024px(*2)
2048px×2048px(*3)
:orig (制限なしか?) 横8000px(?)(*2)
4096px×4096px(*3)

(*1) このサイズ内の画像は原寸で取得できるが、それ以上のものは矩形に収まるよう縮小される(:thumbについては、短辺に合わせてトリミング&リサイズされているものと思われる)。
なお、各種別毎の最大サイズは2016/02/24時点。これまでも何度か変更されている模様。

(*2) 2016/04/13の時点で確認した内容に修正(いつの時点で変更されたかは不明、過去の画像サイズはそのまま)。矩形に収めるようにではなく、横幅を基準に縮小されている?
(*3) 2016/06/05の時点で確認した内容に修正(いつの時点で変更されたかは不明、過去の画像サイズはそのまま)。再び、矩形に収めるように縮小されるようになった(戻った)?


また、試した範囲では、:orig を除く種別については、アップロードした画像の種別(PNG/JPEG)に関わらず、二種類(PNGおよびJPEG)の画像がそれぞれ作成される模様。
これはURL中の拡張子の部分を、pngにしたものjpgにしたものとをダウンロードして調べた。なお、拡張子をgifにしたものもダウンロードできるが、中身はPNG形式のものと同一の模様。

原寸画像(:orig)の画像種別について

:orig については、2016/02/24時点で、表示(ダウンロード)できるのは PNG もしくは JPEG のどちらかに限られる(それ以外は 404 not found となる)。
アップロードした画像の種別と、実際に表示(ダウンロード)出来る画像の関係は以下のようになっている。

アップロード種別 PNG(.png:orig)を表示 JPEG(.jpg:orig)を表示
JPEG ×
PNG(24bpp)PNG(透明ピクセル無し、もしくは約3MBを超えるもの?) ×
PNG(32bpp)PNG(透明ピクセル有り、かつ約3MB以下のもの?) ×

なお、種別に関わらず、Twitter側での画像変換は実施されるため、完全に元画像と同じものではない。

トピックス

原寸画像(:orig)の劣化を抑えるには?(アップロードの前に)

24bpp(アルファチャンネル無し)透明ピクセル無しのPNG画像はJPEGに変換されてしまうため、PNG形式のままで表示(ダウンロード)させたい場合は、32bpp(アルファチャンネル有り)に変換透明ピクセルを追加した後にアップロードするのが今のところ解決方法となる。
ただし、32bpp のPNG 画像が :orig を付けて原寸でダウンロードできた場合も、元画像そのものがダウンロードできるわけではなく、Twitter側で変換されたものになる(アップロードしたものとはファイルサイズ等が異なっている)。

既存の画像については、例えばPNG 32bpp変換 | しぇ庫を使って簡単に24bppから32bppへ変換できる(2016/02/27現在、Twitterの仕様変更により、透明ピクセル追加の対応検討中とのこと→2016/02/28に改訂版がリリースされた)。

原寸画像を表示・保存するためのブラウザ拡張機能/スクリプト

PC 用ブラウザで、Web 版 Twitter の原寸画像を表示したり、ダウンロードしたり出来る拡張機能/ユーザースクリプト等。
furyu.hatenablog.com
いちいち :orig をつけたり、ダウンロード時に拡張子を修正するのが面倒だったので、↓の二つを参考に、独自機能も付け加えて自作したもの。原寸表示・ダウンロード共に対応。Google Chrome、Opera、Firefox(Greasemonkey)に対応(ブックマークレットは一部機能を IE11 にも対応)
hogashi.hatenablog.com
chrome.google.com

試行錯誤など


スマートフォンで公式アプリを使った場合、最大横2048pxあたりまで(アプリ側で)縮められてしまうのかも…?


↑は元画像がJPEG・3264px×2448px(3.39MB)だが、:orig で見ても2048px×1536pxまで縮小させれてしまっている