風柳メモ

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

【覚書】 Twitterのタイムラインなどで、表示位置(スクロール位置)を固定したまま、ポップアップ表示させる方法

Twitter 原寸びゅーで、原寸画像をオーバーレイ(ポップアップ)表示させる際、タイムラインのスクロール位置はそのままにするためにはまっていたので、覚え書き。
つくづく、CSS は苦手やわぁ……。

ヘッダ及び表示範囲に相当する要素を、position: fixed を指定することで固定する、ところまではすぐ思いついたのだが、原寸画像が表示範囲よりも大きい場合にどうすればよいのかが当初は思い付かなかった。

苦肉の策として、オーバーレイ表示時には、

  • 表示範囲を、position: absolute で BODY の上端に合わせる
  • タイムラインについては、ネガティブマージンを設定して、見かけ上、オーバーレイ表示したときと同じ位置のものが表示されるようにする

のようにして対処していた(0.1.5.500まで)

しかし、どうしても無理があったので、パフォーマンスが悪く(反応がにぶく)なってしまった。

その後、

  • 表示範囲は、position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow-y: auto; を指定
  • BODY には、overflow-y: hidden; を指定

のようにしておけば、原寸画像を表示している範囲だけをスクロールさせることが出来ることに気付き、対応。

パフォーマンスについても改善されたように思われる。

なお、このようにした場合、表示範囲の操作(スクロール)に関しては(ブラウザでBODY(HTML)の標準的なキー操作として用意されている)[Space][Home][End]等のキーによる移動は効かなくなるので、自前で用意する必要があった。

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();

独り言など