- 投稿日:2020-04-01T23:05:55+09:00
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/
- 投稿日:2020-04-01T23:05:55+09:00
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/
- 投稿日:2020-04-01T17:42:39+09:00
HTML スクロール時に背景画像を遅らせる
後でコピペできるように残しておきます。
↓わかりにくいですが一応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` })
- 投稿日:2020-04-01T17:22:16+09:00
Google Apps Scriptで快適にWEBスクレイピングするためのライブラリを作ったら、重すぎて動かなかったお話。
作るに至った経緯
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オブジェクトの中身は以下の通り。
treeにはその要素の子要素のリストが、parentにはその要素の親要素への参照が渡されている(図では一番外側のタグのため参照が無い)。下のattrとtagsは検索用の参照のリストが詰まっている。
- 投稿日:2020-04-01T08:04:54+09:00
position:relative・absoluteの使い方
- 投稿日:2020-04-01T05:19:14+09:00
初心者によるプログラミング学習ログ 278日目
100日チャレンジの278日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
278日目は、おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) March 31, 2020
278日目 1.0h
・xdデザインカンプからのサイト模写
・お問い合わせフォーム#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-04-01T00:28:53+09:00
中央寄せの使い分けについて
- 投稿日:2020-04-01T00:03:55+09:00
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で検証しました。
どちらも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を修正する」のどちらかになりそうです。思い込みは良くないですね。
フロントエンドは初心者なので頑張って勉強していきたいです。