20200401のHTMLに関する記事は8件です。

Swiper.jsが動作しなかった時の対処法

はじめに

バージョン確認の重要性

Swiper.jsはバージョンによってサポートしてくれるブラウザが違う。ver5.0以降から、IEは完全にサポート対象外になった。今回私はchromeで開発を行っているので最新版でいけると思いきや、挙動が意図したものにならなかったので、4.5.0系を使うことにした。

便利なオプションたち

オプションが豊富で有名なSwiper.js

ここでは、私が参考にした記事を載せておくだけに留めておく。

参考記事

実例12パターン】画像スライダーはSwiper使っておけば間違いない!実用的な使い方を紹介
https://haniwaman.com/swiper/
swiper.js使ってみたからそのオプションについて(v4.1.6)
https://reiwinn-web.net/2018/03/15/swiper-4-1-6/

実際のコード

swiper.js
$(function(){
  var mySwiper = new Swiper('.swiper-container', {
    slidesPerView: 3,
    slideToClickedSlide: true,
    pagination: {
      el: '.swiper-pagination',
      type: 'bullets',
      clickable: true
    },
    navigation: {
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev'
    },
    breakpoints: {
      767: {
        slidesPerView: 1,
        spaceBetween: 0
      }
    }
  });
});


html.haml
        .swiper-container.p-2.rounded
          .swiper-wrapper.p-1
            - @posts.each do |post| 
              .events__content.col-sm-6.col-md-4.mb-3
                .swiper-slide
                  .card{id: post.id}
                    %label.m-1
                      - if post.image.present?
                        %img.card-img-top.img-fluid.rounded{src: "#{post.image}"}
                      - else
                        %img.card-img-top.img-fluid.rounded{src: "/assets/noimage.png"}
                      .card-body.event
                        %h5= link_to "#{post.title}", post_path(post.id), class: "event-title stretched-link text-decoration-none"
                        .event__name 
                          #{post.user.name} さん
                        .text-right
                          = l post.created_at, format: :long     
           .swiper-button-prev
           .swiper-button-next
           .swiper-pagination

これでprevボタンnextボタンを押しても、スライドされない。consoleをみてもエラーはない。ドラッグしながらスクロールすると、一応.swiper-containerに要素が入っていることはわかる。jsファイルは読み込まれているが、メソッドが実行されていない。

階層構造は崩すな

原因がわかった。先ほどのコードは、.swiper-wrapperと.swiper-slidesの間に、.events__content......というクラスが入っている。これが原因だった。Swiper.jsのswiper-container, wrapper, slidesのクラスたちは必ず親子孫の関係でなくてはならなかった。

修正後のコード

html.haml
.swiper-container.p-2.rounded
          .swiper-wrapper.p-1
            - @posts.each do |post| 
              .swiper-slide.events__content.col-sm-6.col-md-4.mb-3
                .card{id: post.id}
                  %label.m-1
                    - if post.image.present?
                      %img.card-img-top.img-fluid.rounded{src: "#{post.image}"}
                    - else
                      %img.card-img-top.img-fluid.rounded{src: "/assets/noimage.png"}
                    .card-body.event
                      %h5= link_to "#{post.title}", post_path(post.id), class: "event-title stretched-link text-decoration-none"
                      .event__name 
                        #{post.user.name} さん
                      .text-right
                        = l post.created_at, format: :long
                .card.col-auto
                  .text-left
                    - post.genre_list.each do |genre|
                      .badge.badge-primary{data:{role: "tagsinput"}}
                        = link_to "#{genre}", tag_path(genre), class: 'text-decoration-none text-white'
                - if user_signed_in?
                  .offset-8.col-auto.card
                    .text-center.likes
                      = render partial: '/posts/posts', locals: {post: post}
          - if @posts.count >= 4
            .swiper-button-prev
            .swiper-button-next
            .swiper-pagination

これで正常に動くようになった。

終わりに

私のように、こんなことで何時間も無駄にしないように、皆さんも気をつけてください。

参考記事

【Rails5】「Swiper」を使ってスライダー、カルーセルを作る方法
https://qiita.com/emincoring/items/18d07d0aec5d9836227c
[Rails]Swiperで画像スライド作成
https://qiita.com/yummy888/items/8528c7542f85ae7bbc55

サンプル付き!簡単にスライドを作れるライブラリSwiper.js超解説(各種ナビゲーションカスタマイズ編)
https://garigaricode.com/swiper_navigation/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiper.jsが動作しなかった時の対処法[備忘録]

はじめに

バージョン確認の重要性

Swiper.jsはバージョンによってサポートしてくれるブラウザが違う。ver5.0以降から、IEは完全にサポート対象外になった。今回私はchromeで開発を行っているので最新版でいけると思いきや、挙動が意図したものにならなかったので、4.5.0系を使うことにした。

便利なオプションたち

オプションが豊富で有名なSwiper.js

ここでは、私が参考にした記事を載せておくだけに留めておく。

参考記事

実例12パターン】画像スライダーはSwiper使っておけば間違いない!実用的な使い方を紹介
https://haniwaman.com/swiper/
swiper.js使ってみたからそのオプションについて(v4.1.6)
https://reiwinn-web.net/2018/03/15/swiper-4-1-6/

実際のコード

swiper.js
$(function(){
  var mySwiper = new Swiper('.swiper-container', {
    slidesPerView: 3,
    slideToClickedSlide: true,
    pagination: {
      el: '.swiper-pagination',
      type: 'bullets',
      clickable: true
    },
    navigation: {
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev'
    },
    breakpoints: {
      767: {
        slidesPerView: 1,
        spaceBetween: 0
      }
    }
  });
});


html.haml
        .swiper-container.p-2.rounded
          .swiper-wrapper.p-1
            - @posts.each do |post| 
              .events__content.col-sm-6.col-md-4.mb-3
                .swiper-slide
                  .card{id: post.id}
                    %label.m-1
                      - if post.image.present?
                        %img.card-img-top.img-fluid.rounded{src: "#{post.image}"}
                      - else
                        %img.card-img-top.img-fluid.rounded{src: "/assets/noimage.png"}
                      .card-body.event
                        %h5= link_to "#{post.title}", post_path(post.id), class: "event-title stretched-link text-decoration-none"
                        .event__name 
                          #{post.user.name} さん
                        .text-right
                          = l post.created_at, format: :long     
           .swiper-button-prev
           .swiper-button-next
           .swiper-pagination

これでprevボタンnextボタンを押しても、スライドされない。consoleをみてもエラーはない。ドラッグしながらスクロールすると、一応.swiper-containerに要素が入っていることはわかる。jsファイルは読み込まれているが、メソッドが実行されていない。

階層構造は崩すな

原因がわかった。先ほどのコードは、.swiper-wrapperと.swiper-slidesの間に、.events__content......というクラスが入っている。これが原因だった。Swiper.jsのswiper-container, wrapper, slidesのクラスたちは必ず親子孫の関係でなくてはならなかった。

修正後のコード

html.haml
.swiper-container.p-2.rounded
          .swiper-wrapper.p-1
            - @posts.each do |post| 
              .swiper-slide.events__content.col-sm-6.col-md-4.mb-3
                .card{id: post.id}
                  %label.m-1
                    - if post.image.present?
                      %img.card-img-top.img-fluid.rounded{src: "#{post.image}"}
                    - else
                      %img.card-img-top.img-fluid.rounded{src: "/assets/noimage.png"}
                    .card-body.event
                      %h5= link_to "#{post.title}", post_path(post.id), class: "event-title stretched-link text-decoration-none"
                      .event__name 
                        #{post.user.name} さん
                      .text-right
                        = l post.created_at, format: :long
                .card.col-auto
                  .text-left
                    - post.genre_list.each do |genre|
                      .badge.badge-primary{data:{role: "tagsinput"}}
                        = link_to "#{genre}", tag_path(genre), class: 'text-decoration-none text-white'
                - if user_signed_in?
                  .offset-8.col-auto.card
                    .text-center.likes
                      = render partial: '/posts/posts', locals: {post: post}
          - if @posts.count >= 4
            .swiper-button-prev
            .swiper-button-next
            .swiper-pagination

これで正常に動くようになった。

終わりに

私のように、こんなことで何時間も無駄にしないように、皆さんも気をつけてください。

参考記事

【Rails5】「Swiper」を使ってスライダー、カルーセルを作る方法
https://qiita.com/emincoring/items/18d07d0aec5d9836227c
[Rails]Swiperで画像スライド作成
https://qiita.com/yummy888/items/8528c7542f85ae7bbc55

サンプル付き!簡単にスライドを作れるライブラリSwiper.js超解説(各種ナビゲーションカスタマイズ編)
https://garigaricode.com/swiper_navigation/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML スクロール時に背景画像を遅らせる

後でコピペできるように残しておきます。

↓わかりにくいですが一応GIF

ダウンロード.gif

注意

スクロール時の背景画像の滑らかさはモニターのスペックに依存してしまうので、スペックの低いモニターだと結構カクツキます。
ゲーミングモニターだと滑らかに動くので良きですが...

下記のコードではScrollイベントを使って背景画像のポジションを弄っているのですが、カクツキを完全になくすためならrequestAnimationFrameやsetIntervalを使わないとダメかも。

準備

背景に使用する画像を、bg.pngという名前で準備しておきました。

HTML

  <div id="bg" class="ly_bg"></div>

CSS

  .ly_bg {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: -1;
    background-image: url('../images/bg.png');
    background-size: 100% auto;
    background-repeat: repeat-y;
    background-position-y: 0;
  }

JS

let bg = document.getElementById('bg')
const rate = 3

window.addEventListener('scroll', function () {
  let y = window.scrollY
  bg.style.backgroundPositionY = `-${y / rate}px`
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Apps Scriptで快適にWEBスクレイピングするためのライブラリを作ったら、重すぎて動かなかったお話。

efgrhj.png
ウガアッ!

作るに至った経緯

GASでのWEBスクレイピングといえば、送られてきたhtmlソースをただの文字列として受け取り、正規表現やmatch、split、indexOfなどで無理矢理目的の要素・テキストを削り出してくるもの。一応スクレイピング用のライブラリもあるが、やっていることは対して変わらない。

「我々が求めるものはPythonのbs4のような、CSSセレクタなどで簡単に要素を見つけ出し、情報を抽出するものではなかろうか?」

そんなこんなで火が付いた。作ったプロトタイプもGoogle Chromeの開発者ツール上では、少々レスポンスが遅いもののキチンと想定道理に動いていたから、やれる自信はあった。再帰呼び出しの回数が多すぎて、どうやってもGASでは動かないと気付いた時、私は燃え尽きた。

どういうライブラリだったか

CSSセレクタベースで要素を見つけるもの。最初は親・子孫要素や属性([attr="hogehoge"])などにも対応させていたが、少しでも再帰呼び出しを減らすため、どんどんオプションを削られ、しまいには親要素からの検索も露と消えた。まあ結局動かなかったけど。というわけでこのライブラリ(笑)は、Google Chromeの開発者ツール上でしか、筆者は正常に動かしたことがない。悲しい。せっかく作ったのに。

呼び出し

var h = HTMLparser(`
<html>
<head></head>
<body>
<p class="cls">htmlソーステキスト</p>
...
</body>
</html>
`);
var tag_p = h.search("p.cls");
//[{tagname: "p", attr: {…}, innerText: "htmlソーステキスト", tree: Array(1), parent: {…}}]

呼び出し元のコード

function HTMLparser(html_str) {
    if(!(this instanceof HTMLparser)) {
        return new HTMLparser(html_str);
    }else {
        this.html = [];
        this.attr = {};
        this.tags = {};
        var html_obj = {tree: []};
        //str: htmlソース, ary: htmlのツリー, parent: 親要素, self: HTMLparserのthisの参照用
        function per(str, ary, parent, self) {
            var obj = {tagname: "_Node", attr: null, innerText: "", tree: [], parent: null};
            obj.parent = parent;

            //開始タグを見つける
            var matchTag = str.match(/<([a-zA-Z][^\t\n\r\f \/>\x00]*?)(| [a-zA-Z][^\t\n\r\f>\x00]*?[^\/])>([\s\S]*?)$/);
            //[0]: 全体, [1]: タグ名, [2]: 属性, [3]: その開始タグ以降のテキスト

            function sameTagBothReg(tagname) {
                return new RegExp("(<" + tagname + ">|<" + tagname + " [a-zA-Z][^\t\n\r\f>\x00]*?[^\/]>|<\\/" + tagname + ">)");
            }
            function sameTagStartReg(tagname) {
                return new RegExp("(<" + tagname + ">|<" + tagname + " [a-zA-Z][^\t\n\r\f>\x00]*?[^\/]>)");
            }
            function sameTagEndReg(tagname, count) {
                return new RegExp("(<\\/" + tagname + ">)");
            }
            var attrReg = /([a-zA-Z][^\t\n\r\f >\x00]*=\".*?\")/g;
            var attrReg_g = /([a-zA-Z][^\t\n\r\f >\x00]*)=\"(.*?)\"/g;
            if(matchTag) {
                var attr_obj = {};
                var attr_node_list = matchTag[2].split(attrReg).filter(function(r) {return r.match(attrReg);})
                attr_node_list.forEach(function(r) {
                    //タグの属性を収集
                    var a = r.split(attrReg_g);

                    if(!self.attr[a[1]]) {
                        self.attr[a[1]] = {};
                    }

                    var v;
                    //クラスのみリスト化
                    if(a[1] == "class") {
                        v = a[2].split(" ");
                        v.forEach(function(r) {
                            if(!self.attr[a[1]][r]) {
                                self.attr[a[1]][r] = [];
                            }
                            self.attr[a[1]][r].push(obj);
                        });
                    }else {
                        v = a[2];
                        if(!self.attr[a[1]][v]) {
                            self.attr[a[1]][v] = [];
                        }
                        self.attr[a[1]][v].push(obj);
                    }

                    attr_obj[a[1]] = v;
                });
                obj.attr = attr_obj;
                obj.tagname = matchTag[1];

                //その開始タグの対となる終了タグを見つける
                //もし開始タグがspanだった場合、その開始タグ以降のテキストから、
                //spanの開始タグと終了タグをまとめて探索する
                var st_cnt = 1;//開始タグの数(開始タグはすでに一個あるので、初期値は1)
                var ed_cnt = 0;//終了タグの数
                var sp_idx = 0;//目的の終了タグのインデックス
                var splitted_same_tag = matchTag[3].split(sameTagBothReg(matchTag[1]));
                splitted_same_tag.forEach(function(v, i) {
                    if(sp_idx) {
                        return;
                    }
                    //開始タグがマッチしたら+1
                    else if(v.match(sameTagStartReg(matchTag[1]))) {
                        st_cnt++;
                        return;
                    }
                    //終了タグがマッチしたら+1
                    else if(v.match(sameTagEndReg(matchTag[1]))) {
                        ed_cnt++;
                        //開始タグ数と終了タグ数が一致したら、インデックスを記録
                        if(st_cnt == ed_cnt) {
                            sp_idx = i;
                        }else {
                            return;
                        }
                    }
                });
                //始点からインデックスまでが、そのタグの子要素
                var child = splitted_same_tag.slice(0, sp_idx).join("");
                if(matchTag[1] == "title") {
                    self.title = child;
                }
                //scriptタグの中には稀にhtmlのタグが紛れ込んでいるので、
                //誤マッチ回避のため、scriptタグの中身は切り捨て
                if(matchTag[1] !== "script") {
                    //子要素をターゲットにして自身(obj)を親とし、perを再帰的に呼び出し
                    per(child, obj.tree, obj, self);
                }
                //インデックスから終点までが、そのタグの兄弟要素
                var bro = splitted_same_tag.slice(sp_idx + 1).join("");
                if(bro !== "") {
                    //兄弟要素をターゲットにして自身と同じ親要素(parent)
                    //を親とし、perを再帰的に呼び出し
                    per(bro, ary, parent, self);
                }
            }else {
                obj.tree = [str];
            }

            ary.unshift(obj);

            //もしタグ名が見つからなかったら(_Nodeのままなら)、
            //自身と親・先祖のinnerTextに中身の文字列を追加
            if(obj.tagname == "_Node") {
                Text(obj, obj.tree.join(""));
                function Text(o, txt) {
                    o.innerText += txt;
                    if(o.parent) {
                        Text(o.parent, txt);
                    }
                }
            }else {
                if(!self.tags[obj.tagname]) {
                    self.tags[obj.tagname] = [];
                }
                self.tags[obj.tagname].push(obj);
            }

        }
        per(html_str.replace(/<!--[\s\S]*?-->/g, ""), this.html, null, this);
    }
}

//検索機能(最も削り取られた部分)
HTMLparser.prototype.search = function(selector) {
    var sel = sel_parse(selector);
    //終端の1要素のみ読み取る
    //CSSセレクタは「右から」読み取るのが効率的
    return check_tree(sel[0], this);

    function check_tree(s, self) {
        var r = [];
        var _tag = s.tag;
        if(_tag) {
            if(self.tags[_tag]) {
                for(var t of self.tags[_tag]) {
                    var _atr = t.attr;
                    if(s.class.length) {
                        if(_atr.class) {
                            var _chk = false;
                            for(var cls of s.class) {
                                if(_atr.class.indexOf(cls) < 0) {
                                    _chk = true;
                                    break;
                                }
                            }
                            if(_chk) {continue;}
                        }else {
                            continue;
                        }
                    }
                    if(s.id.length) {
                        if(s.id[0] !== _atr.id) {
                            continue;
                        }
                    }
                    r.push(t);
                }
            }
        }else {
            var c_ary = [];
            var i_ary = [];
            var a_ary = [];//削り取られた残滓

            if(s.class.length) {
                var _chk = true;
                var clsList = Object.keys(self.attr.class);
                for(var cls of s.class) {
                    if(clsList.indexOf(cls) < 0) {
                        break;
                    }else {
                        if(!c_ary.length) {
                            c_ary = self.attr.class[cls];
                        }else {
                            c_ary = c_ary.concat(self.attr.class[cls]).filter(function(x, i, self) {
                                return self.indexOf(x) === i && i !== self.lastIndexOf(x);
                            });
                        }
                    }
                }
            }
            if(s.id.length) {
                var idList = Object.keys(self.attr.id);
                if(-1 < idList.indexOf(s.id[0])) {
                    i_ary = self.attr.id[s.id[0]];
                }
            }
            if(s.attr.length) {
                var _chk = false;
                for(var a of s.attr) {
                    var _k = a.atr;
                    var _m = false;
                    if(_k.match(/(\*|\^|\$)$/)) {
                        _m = _k.match(/(\*|\^|\$)$/)[1];
                        _k = _k.replace(/(\*|\^|\$)$/, "");
                    }
                }
            }
            var full_cnt = 0;
            if(c_ary.length) {
                full_cnt++;
                r = r.concat(c_ary);
            }
            if(i_ary.length) {
                full_cnt++;
                r = r.concat(i_ary);
            }
            if(a_ary.length) {
                full_cnt++;
                r = r.concat(a_ary);
            }
            if(1 < full_cnt) {
                r = r.filter(function(x, i, self) {
                    return self.indexOf(x) === i && i !== self.lastIndexOf(x);
                });
            }
        }
        return r;
    }

    //CSSセレクタの解析用
    function sel_parse(sel) {
        if(sel.match(/ ?[\+\~] ?/g)) {
            throw Error('You cannot use Adjacent sibling combinator "+/~".');
        }
        else if(sel.match(/\:(nth-child\(|nth-of-type\(|not\(|first-child|first-of-type|last-child|last-of-type)/g)) {
            throw Error('You cannot use Pseudo-elements like ":nth-of-type()"');
        }
        var sp = sel.split(/( ?> ?|(?<=[a-zA-Z0-9\]\_\-]) (?=[a-zA-Z\[\.\#\_]))/g);
        var a = [], nxt = false;
        sp.forEach(function(s, idx) {
            if(s.match(/^ $/g)) {
                return;
            }
            else if(s.match(/^ ?> ?$/g)) {
                nxt = true;
                return;
            }
            var _o = {"tag": null, "class": [], "id": [], "attr": [], "next": false};
            if(nxt) {
                _o.next = true;
                nxt = false;
            }
            var _s = s.split(/(\[.*?\]|(?<=(?:[a-zA-Z\]]|^))(?:\.|\#)[a-zA-Z\_][a-zA-Z0-9\_\-]*)/g).filter(function(r) {return r;});
            _s.forEach(function(p) {
                if(p.match(/^\#/)) {
                    _o.id.push(p.replace(/\#/, ""));
                }
                else if(p.match(/^\./)) {
                    _o.class.push(p.replace(/\./, ""));
                }
                else if(p.match(/^[a-zA-Z]/)) {
                    _o.tag = p;
                }
                else if(p.match(/^\[(.*?)(?:\=(?:\"(.*?)\"|\'(.*?)\')|)\]/)) {
                    var _m = p.match(/^\[(.*?)(?:\=(?:\"(.*?)\"|\'(.*?)\')|)\]/);
                    _o.attr.push({"atr": _m[1], "que": _m[2] ? _m[2] : ""});
                }
            });
            a.unshift(_o);
        });
        return a;
    }
}

ちなみに、HTMLparserオブジェクトの中身は以下の通り。
dfdgfh.png
treeにはその要素の子要素のリストが、parentにはその要素の親要素への参照が渡されている(図では一番外側のタグのため参照が無い)。下のattrとtagsは検索用の参照のリストが詰まっている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

position:relative・absoluteの使い方

position→要素の位置を決めるためのもの

:shamrock:relative:

現在の位置を基準に相対的な位置を決める

:shamrock:absolute:

親要素を基準に絶対的な位置を決める
:arrow_forward:絶対的というのは他の要素や余白の有無など他のものに影響されることなく、
その位置に絶対に配置されるということ

<使い方>

①親要素をposition:relativeにして、現在の位置に堂々といてもらう

②動かしたいやつをposition:absolute;にして、親要素基準の位置をtopとかできめる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 278日目

100日チャレンジの278日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
278日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

中央寄せの使い分けについて

text-align:centerはインライン要素担当!

ただし、親はブロック要素じゃないと効かない
そして親要素にtext-alignをかく


margin 0 auto;はブロック要素担当!

親要素と同じ大きさだったらwidthで横幅を指定しないと中央寄せにならない

※autoは余白を自動で調整してくれるやつ
左右にautoを指定すると左右中央寄せになる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

asyncとdeferの仕様を誤解していた。

解決したい問題

勤務先のWebサイトで、他社製のスクリプトAの実行が完了する前に、自社製のスクリプトBが実行されるとIE11がクラッシュするバグが見つかりました。具体的には以下のように呼び出しています。
なお、A.jsの呼び出し用タグは他社からの指示で1文字たりとも変更できません。

<html>
  <head>
    <!-- 自社製スクリプト -->
    <script src="./B.js"></script>
    <!-- 他社製スクリプト -->
    <script src="./A.js" async></script>
  </head>
  <body>
  </body>
</html>

クラッシュする原因は「B.jsの作りに問題があり、その問題をIE11だけが踏み抜くから」なのですが、A.jsの実行が完了していればクラッシュは回避できるため、B.jsがA.jsの実行を待てば良いと思い、以下のように書換えました。

<html>
  <head>
    <!-- 他社製スクリプト -->
    <script src="./A.js" async></script>
    <!-- 自社製スクリプト -->
    <!-- deferはasyncの完了も待ってくれることを期待した -->
    <script src="./B.js" defer></script>
  </head>
  <body>
  </body>
</html>

仕様を誤解していた箇所

deferはasyncの完了を待ってくれると勝手に思い込んでいたのですが、クラッシュしたりしなかったりする挙動になってしまい、状況が悪化しました。
WHATWGの規格書を読んでもどうしてもピンと来なかったため、検証用のコードを書くことにします。
挙動さえわかればいいのでコード自体はめちゃくちゃ適当です。色んな人から怒られそうなコードですね。

検証用コード

html

<html>
  <head>
    <script src="./first.js" async></script>
    <script src="./second.js" defer></script>
  </head>
  <body>
  </body>
</html>

asyncで実行するjs (first.js)

var i = 0;
for (i = 0; i < 500000000; i++) { }
console.log("first.js: " + i);

deferで実行するjs (second.js)

console.log("second.js: " + i);

検証結果

Chrome 80で検証しました。

スクショ
3回に1回位はエラーが出るという感じです。

どちらもdeferがついていれば想定通りfirst->secondの順で呼び出されますが、asyncとdeferだと必ずasync->deferになるわけではないのですね。
実際に検証した後に <script> タグに async / defer を付けた場合のタイミング - Qiita を読むと大変わかりやすかったです。というか最初からこちらの記事を読んでいれば無駄に検証しなくても良かったのでは…。

以下も検証しましたがやはりNGでした。10回に1回位はエラーが出ます。

<html>
  <head>
    <script src="./first.js" async></script>
  </head>
  <body>
    <script src="./second.js"></script>
  </body>
</html>

ということで、今の所思いつく解決策としては「A.jsの作成元にdeferで呼び出していいか確認する」か、「自社スクリプトB.jsを修正する」のどちらかになりそうです。思い込みは良くないですね。
フロントエンドは初心者なので頑張って勉強していきたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む