とりあえず書きなぐる

とりあえず、マイノリティーなプログラムを極力説明せずに書きなぐります

エラーメッセージ表示

入力のエラーメッセージを用意します
f:id:vzc00525:20190502210030p:plain

大した話ではないです
/Src/cm.ts

    /*
     * エラーバルーン設定
     */
    export function errorBallon(selector?: string | JQuery, message?: string) {
        let target = $(".ui-error");
        if (selector) {
            target = $(selector);
        }
        if (message) {
			message = message.replace(/\r?\n/g, "<br>");	// 改行コードBR変換
            target.html(message).addClass("ui-error-ballon");
        } else {
            target.html("").removeClass("ui-error-ballon");
        }
    }

/Src/cm.css

/*
  Error Ballon
*/

body .ui-error {
  display: none;
  margin: 0;
}

  body .ui-error.ui-error-ballon {
    margin-top: 0;
    color: white;
    position: relative;
    background: red;
    border: 1px solid red;
    border-radius: 0;
    padding: 3px 0.5em;
    display: inline-block;
    min-width: 5em;
    min-height: 1em;
  }
    body .ui-error.ui-error-ballon:before {
      bottom: 100%;
      border: solid transparent;
      content: " ";
      height: 0;
      width: 0;
      position: absolute;
      pointer-events: none;
      border-bottom-color: red;
      border-width: 8px;
      left: 30px;
      margin-left: -8px;
    }

エラー位置までスクロールするためのメソッドも追加します
/Src/cm.css

namespace cm {
    "use strict";

	/*
	 * スクロール位置設定
	 */
	export function setScrollBottom(item?: JQuery) {
		if (item) {
			let pg = cm.getActivePage();
			let h1 = pg.children(".ui-header").outerHeight();
			let h2 = pg.children(".ui-footer").outerHeight();

			let windowTop = $(document).scrollTop() + h1;
			let windowHeight = $(window).height() - h1 - h2;

			let itmTop = item.get(0).offsetTop;
			let itmHeight = item.height();

			if (windowTop > itmTop) {
				$.mobile.silentScroll(itmTop - h1);
			}
			if ((windowTop + windowHeight) < (itmTop + itmHeight)) {
				$.mobile.silentScroll((itmTop + itmHeight) - windowHeight);
			}
		} else {
			$.mobile.silentScroll($(document).height());
		}
	}
	/*
	 * スクロール位置判定
	 */
	export function isScrollTop(margin?: number): boolean {
		if (margin === undefined) {
			margin = 0;
		}

		if ($(window).scrollTop() <= margin) {
			return true;
		}
		return false;
	}
	/*
	 * スクロール位置判定
	 */
	export function isScrollBottom(margin?: number) {
		if (margin === undefined) {
			margin = 0;
		}

		if ($(window).scrollTop() + $(window).height() >= $(document).height() - margin) {
			return true;
		}
		return false;
	}

    ....

}

サンプルはhomeのpage2でOKボタン押下時にエラーをチェックして表示とします
/Src/home.ts

    <div role="main" class="ui-content ui-form">

      <form novalidate="novalidate" style="display:none;" id="dummy_form" title="Firefox form validate対応"></form>

      <ul data-role="listview" data-inset="true" data-icon="false" class="split">
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_text12">Text input:</label>
            <div>
              <input name="page2_text12" id="page2_text12" type="text" value="">
            </div>
          </div>
          <button class="ui-btn ui-btn-icon-notext ui-btn-split ui-icon-carat-d" cmd="list"></button>
          <label class="ui-error" id="page2_text12_error"></label>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_text13">Text input clr:</label>
            <div>
              <input name="page2_text13" id="page2_text13" type="text" value="" data-clear-btn="true">
            </div>
          </div>
          <label class="ui-error" id="page2_text13_error"></label>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_spin1">Spin input:</label>
            <div>
              <input name="page2_spin1" id="page2_spin1" type="number" value="" max="100" min="-100" step="2" form="dummy_form">
            </div>
          </div>
          <label class="ui-error" id="page2_spin1_error"></label>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_spin2">Spin input clr:</label>
            <div>
              <input name="page2_spin2" id="page2_spin2" type="number" value="" max="100" min="0" step="0.5" data-clear-btn="true" form="dummy_form">
            </div>
          </div>
          <label class="ui-error" id="page2_spin2_error"></label>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_search8">Search:</label>
            <div>
              <input name="page2_search8" id="page2_search8" type="search" value="">
            </div>
          </div>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_textarea12">Textarea:</label>
            <div>
              <textarea name="page2_textarea12" id="page2_textarea12" rows="8" cols="40"></textarea>
            </div>
          </div>
          <button class="ui-btn ui-btn-icon-notext ui-btn-split ui-icon-delete" cmd="clear"></button>
          <label class="ui-error" id="page2_textarea12_error"></label>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="page2_flip1">Flip switch:</label>
            <div>
              <input name="page2_flip1" id="page2_flip1" type="checkbox" data-role="flipswitch">
            </div>
          </div>
          <label class="ui-error" id="page2_flip1_error"></label>
        </li>
      </ul>
    </div>

FireFoxだと数値入力でsubmitが勝手に実行されるのでダミーフォームを用意して結び付けてあります
どんな理屈かはわかりません
エラーメッセージはCSSで初期値非表示となります

OKボタン押下イベント
/Src/home.ts

namespace Home {
	"use strict";

        ....

	namespace Page2 {

            ....

        $(document).on("click", "#page2 > .ui-footer .ui-btn", function (event: Event) {
            $(this).blur();
            cm.errorBallon();	// エラー表示初期化

            let cmd = $(this).attr("cmd");
            switch (cmd) {
                case "reset":
                    let y2 = $("#page2 > .ui-footer").outerHeight() - $(window).scrollTop();
                    Popup.open("#page2_popup1", {
                        x1: 0
                        , y2: y2
                        , transition: "flip"
                        , positionFixed: true
                    });
                    break;
                case "canel":

                    break;

                case "apply":

                    let errLi: JQuery = null;

                    if (!$("#page2_text12").val()) {
                        cm.errorBallon("#page2_text12_error", "文字を入力して下さい");

                        if (!errLi) {
                            errLi = $("#page2_text12").closest("li");
                        }
                    }

                    if (!$("#page2_text13").val()) {
                        cm.errorBallon("#page2_text13_error", "文字を入力して下さい");

                        if (!errLi) {
                            errLi = $("#page2_text13").closest("li");
                        }
                    }

                    if (parseFloat($("#page2_spin1").val()) === 0) {
                        cm.errorBallon("#page2_spin1_error", "数値を入力して下さい");

                        if (!errLi) {
                            errLi = $("#page2_spin1").closest("li");
                        }
                    }

                    if (parseFloat($("#page2_spin2").val()) === 0) {
                        cm.errorBallon("#page2_spin2_error", "数値を入力して下さい");

                        if (!errLi) {
                            errLi = $("#page2_spin2").closest("li");
                        }
                    }

                    if (!$("#page2_textarea12").val()) {
                        cm.errorBallon("#page2_textarea12_error", "何かを入力して下さい");

                        if (!errLi) {
                            errLi = $("#page2_textarea12").closest("li");
                        }
                    }

                    if ($("#page2_flip1").checked() !== true) {
                        cm.errorBallon("#page2_flip1_error", "ONにして下さい");

                        if (!errLi) {
                            errLi = $("#page2_flip1").closest("li");
                        }
                    }
                    // エラー位置までスクロール
                    if (errLi) {
                        cm.setScrollBottom(errLi);
                    }

                    break;
            }
        });
    }
}

片っ端からidを付けるのも無理があるので親一つにidを付け、attrで判別するようにします
エラーな場合は親のliを保持してそこまでスクロールします