質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

7217閲覧

React.jsでconfirmダイアログを使った時のスマホブラウザ間での動作の違いについて

moriman

総合スコア615

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2019/08/30 03:07

class ImputSomethingWithConfirm extends React.Component { constructor(props) { super(props); this.state = { input_state:false }; } onfocus(e){ let result = confirm(`変更しようとしています。 よろしければ「OK」を押してください。 変更を意図していない場合(間違いの場合) 「キャンセル」→「完了」を押してください。`); if( result ) { this.setState({input_state:true}); } else { this.setState({input_state:false}); } } onblur(e){ this.setState({input_state:false}); } render(){ return ( <div> {this.state.input_state ? <div> <p className="input_label">{this.props.name_of_button}(入力OK)</p>:<input type="text" className="input" name={this.props.nog} onChange={this.props.onClick} onBlur={(e)=>this.onblur(e)}/><p className="input_label">{this.props.name_of_unit}</p> </div> : <div> <p className="input_label">{this.props.name_of_button}(入力NG)</p>:<input type="text" className="input" name={this.props.nog} onChange={this.props.onClick} onFocus={(e)=>this.onfocus(e)}/><p className="input_label">{this.props.name_of_unit}</p> </div> } </div> ) } }

React.jsで入力ボックスコンポーネントなんですが、onFocus時にconfirm関数で、入力値を変更しようとしていることをユーザに知らせる(確認を促す)ものを作りたいです。
とりあえず作ってみたのが↑です。

最初はstate.input_stateがfalseなのでonFocus時にconfirm関数により注意ダイアログが出る入力ボックス(入力NGの方)を表示。ダイアログでOKを押すとstate.input_stateがtrueにセットされ、ダイアログが出ない(入力可能な)入力ボックス(入力OKの方)が表示される。
入力後onBlur時にstate.input_stateをfalseにして、次のonFocus時には注意ダイアログが出るようにする。

という感じです。これでスマホのchromeだとおおむね期待通りの動きなのですが、safariで動かしてみると、
入力しようとする(onFocus)→ダイアログ出現→ダイアログで「OK」を押す→ダイアログ消える、しかし表示されるのは注意ダイアログが出る入力ボックス(入力NGの方)→入力しようとする→ダイアログ出現→以下ループ

となり、いつまでたっても入力できません。

原因がわかりません。当たり前なのだと思いますが、簡単なコードで、safariでもconfirmのダイアログでOKを押したらtrueが返されるのは確認しました。

あとブラウザの仕様が原因、ということだとconfirmを使わない別の方法を考える、ということになるでしょうか?何かこういう感じの既成コンポーネントとかありますでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんにちは。

create-react-app でアプリを作成し、ご質問に挙げられている ImputSomethingWithConfirm をそのままコピペしてマウントさせると、確かにご質問にあるような(悩ましい) 状況を再現できました。
(ちなみに、スマホのブラウザを使わなくても、PCのChromeで確認できました)

原因は、confirmダイアログのOKボタンまたはキャンセルボタンをクリック(タップ)してダイアログが消えるときにも、focusイベント や blur イベントが発生していることと考えられます。

そのため、confirmダイアログが表示されている間は、onfocus と onblur を無効にして、confirmダイアログが消えた後に有効にするといったような処理が必要ですが、confirm ダイアログが表示されている、または消えているという2状態と完全に同期したフラグを this.state の中に持たせることは困難なので、苦肉の策が必要と思います。

一例として、以下のように修正してみました。

jsx

1import React from 'react'; 2 3class ImputSomethingWithConfirm extends React.Component { 4 constructor(props) { 5 super(props); 6 this.state = { 7 input_state:false 8 }; 9 this.onblur = this.onBlurCallback; 10 this.onfocus = this.onFocusCallback; 11 } 12 13 afterConfirm() { 14 setTimeout(() => { 15 this.onblur = this.onBlurCallback; 16 this.onfocus = this.onFocusCallback; 17 }, 300); 18 } 19 20 onFocusCallback(e){ 21 this.onblur = () => {}; 22 this.onfocus = () => {}; 23 let result = window.confirm(`変更しようとしています。 24よろしければ「OK」を押してください。 25変更を意図していない場合(間違いの場合) 26「キャンセル」→「完了」を押してください。`); 27 if( result ) { 28 this.setState({input_state:true}); 29 } 30 else { 31 this.setState({input_state:false}); 32 e.target.blur(); 33 } 34 this.afterConfirm(); 35 } 36 37 onBlurCallback(e){ 38 this.setState({input_state:false}); 39 } 40 41 render(){ 42 43 return ( 44 <div> 45 {this.state.input_state ? 46 <div> 47 <p className="input_label">{this.props.name_of_button}(入力OK)</p>:<input type="text" className="input" name={this.props.nog} onChange={this.props.onClick} onBlur={(e)=>this.onblur(e)}/><p className="input_label">{this.props.name_of_unit}</p> 48 </div> 49 : 50 <div> 51 <p className="input_label">{this.props.name_of_button}(入力NG)</p>:<input type="text" className="input" name={this.props.nog} onChange={this.props.onClick} onFocus={(e)=>this.onfocus(e)}/><p className="input_label">{this.props.name_of_unit}</p> 52 </div> 53 54 } 55 </div> 56 57 ) 58 } 59 60} 61

上記で、 render() は、ご質問に挙げられているコードから修正していません。
念のため、上記の修正版の ImputSomethingWithConfirm を動作確認して、PCでもスマホのブラウザ(確認に使用したのは、iPhone8 の Chrome と Safari)でも期待される動作に(一応)なっているかなと思えました。

とはいえ、やっていることがかなりトリッキーですし、ダイアログにスタイルをあてることもできないので、やはりダイアログコンポーネントを自作して、表示、非表示のフラグを this.state の中に持てるようにしたほうがよいように思えます。

以下、ダイアログを自作する際のベースとなるコンポーネントとして、候補になりそうなものを挙げます。

  • react-modal --- Qiita にも react-modal を使ってモーダルを作りました、という記事がいくつかあります。

  

  

  • Material UIDialog --- ダイアログの他にも様々なUI部品が用意されていますが、Material UI全般の学習コストが高めです。

  

以上、参考になれば幸いです。

投稿2019/08/30 17:17

編集2019/08/31 04:10
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

moriman

2019/08/31 07:16

いつもありがとうございます。 既成コンポーネントの使い方を覚えた方が良さそうですね。 すみません、新しい質問の方もよろしければお願い致します。
jun68ykt

2019/08/31 07:30

どういたしまして。 > 既成コンポーネントの使い方を覚えた方が良さそうですね。 はい。それをお勧めします。新しい質問のほうも拝見しますね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問