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

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

ただいまの
回答率

87.92%

Uncaught DOMExceptionというエラーについて

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,862

score 44

お世話になります。

Wordpressで「WooCommerce Bookings」を使用しているのですが、
先日からWordpressのdatepicker.min.jsを利用したカレンダー表示が
Chromeで機能しなくなりました。Firefoxでは表示されています。

Chromeのデベロッパーツールでは以下のようにエラーが表示されています。

Uncaught DOMException: Failed date-picker.min.js
to execute 'btoa' on 'Window': 
The string to be encoded contains characters outside of the Latin1 range.


該当箇所は以下の部分です。

var cacheKey = btoa( date.toString().replace( /[\u00A0-\u2666]/g, function( c ) {
            return '&#' + c.charCodeAt( 0 ) + ';';


該当箇所(cacheKey)を含む一連のコードです。

    /**
     * Applys attributes to the dates on the datepicker. This is necessary since we cant
     * defer beforeShowDay until after our data is loaded.
     * 
     * @version 1.10.11
     * @since   1.10.11
     * @param   {object} dateRange - Date range for attributes to be applied to.
     */
    WC_Bookings_DatePicker.prototype.applyStylesToDates = function applyStylesToDates( dateRange ) {

        var checkDate = dateRange.startDate;

        while ( checkDate < dateRange.endDate ) {

            var cacheKey = btoa( checkDate.toString().replace( /[\u00A0-\u2666]/g, function( c ) {
                return '&#' + c.charCodeAt( 0 ) + ';';
            } ) );

            if ( ! this.cache.attributes[ cacheKey ] ) {

                var attributes    = this.getDateElementAttributes( checkDate );
                var selector    = $( 'td[data-month="' + checkDate.getMonth() + '"] a', this.$picker ).filter(function() {
                    return $(this).text() == checkDate.getDate();
                }).parent();

                if ( ! attributes.selectable ) {
                    attributes.class.push( 'ui-datepicker-unselectable' );
                    attributes.class.push( 'ui-state-disabled' );
                }

                if ( checkDate.setHours( 0, 0, 0, 0 ) === new Date().setHours( 0, 0, 0, 0 ) ) {
                    attributes.class.push( 'ui-datepicker-today' );
                }

                $.each( attributes, function( key, val ) {

                    selector.attr( key, ( $.isArray( val ) ) ? val.join( ' ' ) : val );

                });

                if ( this.opts.cache ) {
                    this.cache.attributes[ cacheKey ] = attributes;
                }

            }

            checkDate.setDate( checkDate.getDate() + 1 );

        }

    }

    /**
     * If caching is being requested beforeShowDay will use this method to load styles from cache if available.
     *
     * @version 1.10.11
     * @since   1.10.11
     * @param   {object} date - Date to apply attributes to.
     */
    WC_Bookings_DatePicker.prototype.maybe_load_from_cache = function maybe_load_from_cache( date ) {

        var cacheKey = btoa( date.toString().replace( /[\u00A0-\u2666]/g, function( c ) {
            return '&#' + c.charCodeAt( 0 ) + ';';
        } ) );

        var defaultClass        = ( '1' === this.customData.default_availability ) ? 'bookable' : 'not-bookable' ;
        var attributes            = [ true, defaultClass, '' ];
        var cachedAttributes    = this.cache.attributes[ cacheKey ];

        if ( cachedAttributes ) {
            cachedAttributes = [ cachedAttributes.selectable, cachedAttributes.class.join( ' ' ), cachedAttributes.title ];
        } else if ( this.bookingsData ) {
            var attrs = this.getDateElementAttributes( date );
            attributes = [ attrs.selectable, attrs.class.join(' '), attrs.title ];
        }

        return cachedAttributes || attributes;

    }

    /**
     * Returns the default parameters.
     *
     * @version 1.10.11
     * @since   1.10.11
     */
    WC_Bookings_DatePicker.prototype.get_default_params = function get_default_params() {

        return this.defaultParams || {};

    }

    /**
     * Set and override the default parameters.
     * 
     * @version 1.10.11
     * @since   1.10.11
     * @param   {object} params - Parameters to be set or overridden.
     */
    WC_Bookings_DatePicker.prototype.set_default_params = function set_default_params( params ) {

        var _defaultParams = {
            showWeek            : false,
            showOn                : false,
            numberOfMonths        : 1,
            showButtonPanel        : false,
            showOtherMonths        : true,
            selectOtherMonths    : true,
            gotoCurrent            : true,
            dateFormat            : $.datepicker.ISO_8601,
        }

        if ( typeof params !== 'object' ) {
            throw new Error( 'Cannot set params with typeof ' + typeof params );
            return;
        }

        this.defaultParams = $.extend( _defaultParams, params ) || {};

    }

    /**
     * Get the data from the server for a block of time.
     * 
     * @since   1.10.11
     * @param   {string} year - Year being requested.
     * @param   {string} month - Month being requested.
     * @returns {object} Deferred object to be resolved after the http request
     */
    WC_Bookings_DatePicker.prototype.get_data = function get_data( year, month ) {
        year  = undefined !== year  ? year  : new Date().getFullYear();
        month = undefined !== month ? month : new Date().getMonth() + 1;

        /**
         * Overlay styles when jQuery.block is called to block the DOM.
         */
        var blockUIOverlayCSS = {
            background: '#fff',
            opacity: 0.6,
        };

        /**
         * Get a date range based on the start date.
         *
         * @since   1.10.11
         * @param   {string} startDate - Optional start date to get the date range from.
         * @returns {object} Object referencing the start date and end date for the range calculated.
         */
        var get_date_range = function get_date_range( startDate ) {

            if ( ! startDate ) {
                startDate = new Date( [ year, month, '01' ].join( '/' ) );
            }

            var range = this.get_number_of_days_in_month( month );
            return this.get_padded_date_range( startDate, range );

        }.bind(this);

        var deferred    = $.Deferred();
        var dateRange   = get_date_range();

        startDateString = dateRange.startDate.toString()
        startDateString = btoa( startDateString.replace( /[\u00A0-\u2666]/g, function( c ) {
            return '&#' + c.charCodeAt( 0 ) + ';';
        } ) );

        endDateString = dateRange.endDate.toString();
        endDateString = btoa( endDateString.replace( /[\u00A0-\u2666]/g, function( c ) {
            return '&#' + c.charCodeAt( 0 ) + ';';
        } ) );

        var cacheKey    = startDateString + endDateString;

        if ( this.opts.cache && this.cache.data[ cacheKey ] ) {

            deferred.resolveWith( this, [ dateRange, this.cache.data[ cacheKey ] ] );

        } else {

            var params = {
                'product_id': this.get_custom_data( 'product_id' ),
                'wc-ajax'    : 'wc_bookings_find_booked_day_blocks',
                'security'    : this.$form.data( 'nonce' ),
            }

            this.$picker.block( {
                message: null,
                overlayCSS: blockUIOverlayCSS,
            } );

            params.min_date = dateRange.startDate;
            params.max_date = dateRange.endDate;

            $.ajax({
                context: this,
                url: wc_bookings_date_picker_args.ajax_url,
                method: 'GET',
                data: params,
            })
            .done( function( data ) {

                this.bookingsData = this.bookingsDate || {};

                $.each( data, function( key, val ) {

                    if ( $.isArray( val ) || typeof val === 'object' ) {

                        var emptyType = ( $.isArray( val ) ) ? [] : {};

                        this.bookingsData[ key ] = this.bookingsData[ key ] || emptyType;

                        $.extend( this.bookingsData[ key ], val );

                    } else {

                        this.bookingsData[ key ] = val;

                    }

                }.bind( this ) );

                this.cache.data[ cacheKey ] = data;

                if ( ! year && ! month && this.bookingsData.min_date ) {
                    dateRange = get_date_range( this.get_default_date( this.bookingsData.min_date ) );
                }

                deferred.resolveWith( this, [ dateRange, data ] );

                this.$picker.unblock();

            }.bind( this ) );

        }

        return deferred;

    }

どうもChrome自体の問題のようなのですが(Firefoxの開発ツールではエラーは表示されません)エラー内容で検索しても目ぼしい情報が得られないためお知恵をお借りしたいです。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • think49

    2018/09/24 10:45 編集

    現象を再現可能なコードを開示して下さい。ちなみに、Firefoxで現象発生しない=Chromeのバグと考えるのは早計です。エラーになるChromeが正しくて、Firefoxのバグである可能性もありますよ。

    キャンセル

  • MyQuestioner

    2018/09/24 12:07

    think49様、返信ありがとうございます。なるほどChromeが正しいという訳ですね。ちなみにカレンダーが表示されるのはFirefoxとiPhoneやiPadなどのsafariブラウザです。デベロッパーツールでエラーが指摘される周辺のコードを記載いたしました。文字数の関係上date-picker.min.jsのコード全てを記載できないので、一部分だけでは分かり難いかと思いますがご容赦下さい。

    キャンセル

回答 2

checkベストアンサー

0

 "InvalidCharacterError" DOMException

Uncaught DOMException: Failed date-picker.min.js
to execute 'btoa' on 'Window': 
The string to be encoded contains characters outside of the Latin1 range.

DOMException は DOM で規定されている例外エラーの一つです。
原因はエラーメッセージに書かれている通り、Latin1 範囲外の文字列を指定しているからです。
簡単な英語なので、エラーメッセージを解読しましょう。

仕様上でも言及されています。

result = self . btoa( data )
入力データを取得し、U+0000からU+00FFまでの範囲で文字のみを含むUnicode文字列の形式で、それぞれ値0x00から0xFFまでを持つバイナリーバイトを表し、そのbase64表現に変換したものを返す。
Throws an "InvalidCharacterError" DOMException exception if the input string contains any out-of-range characters.


どうもChrome自体の問題のようなのですが(Firefoxの開発ツールではエラーは表示されません)

従って、Firefoxのバグです。
DOMException のエラーを返す、Google Chrome の動作が正しい。

 対策

対策はMDNで言及されている通り、[0x00-0xFF] の範囲内に収まるようにエンコードすれば良いのですが、

バイナリデータなら範囲内に収まるはずなので、用途が誤っている予感があります。

Re: MyQuestioner さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/24 12:25

    think49様、詳細な回答ありがとうございます。
    エラーメッセージが何も示しているのかは分かったのですが、具体的にどうしてよいか分からないというのが実情です。
    つい先日までChromeで表示されていたものが急に表示されなくなる…。
    Chromeをアップデートさせたわけではありませんし、プラグインの更新をした訳でもない…。
    キャッシュプラグインを使用していたので、しばらく古いキャッシュを見ていたため、Chromeの仕様変更に気が付かなかったということも考えられます。
    >バイナリデータなら範囲内に収まるはずなので、用途が誤っている予感があります。
    そうなりますとブラウザの問題ではなくコードに問題があるということののですね…。正直さっぱりなのですが、何か良い方法はありますでしょうか?
    teratailは答えを聞く場でないことは重々分かっているのですが、今回のはちょっと自分の能力では厳しいです。。。

    キャンセル

0

解決したといえるかどうかわかりませんが、Chromeの言語設定を英語に変更したところエラーが表示されなくなり、カレンダーが表示されるようになりました。(日本語に戻すとダメです…)
Wordpressの多言語化プラグインが上手く作動していないか、設定に間違いがあるのかもしれません。
think49様には、大変丁寧な解説をして頂き、誠にありがとうございました。
直接の解決策でなくてもありがたかったです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る