<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css">
<link rel="stylesheet" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<header>
遅延ロードサンプル
</header>
<main>
<ul class="content-root">
</ul>
</main>
<script src="./RssFeed.js"></script>
<script src="./main.js"></script>
html,
body {
width: 100%;
height: 100%;
}
header {
display: block;
background-color: steelblue;
color: white;
font-size: large;
text-align: center;
line-height: 48px;
height: 48px;
position: relative;
z-index: 101;
box-shadow:rgb(0 0 0 / 20%) 0px 2px 4px -1px,
rgb(0 0 0 / 14%) 0px 4px 5px 0px,
rgb(0 0 0 / 12%) 0px 1px 10px 0px
}
main {
background-color: whitesmoke;
overflow-y: scroll;
height: calc(100% - 48px);
}
.content-root {
display: grid;
margin: 1em 0;
padding: 0 1em;
border: none;
column-gap: 1.5em;
row-gap: 1.2em;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-auto-rows: 12em;
}
/* feed */
.feed {
position: relative;
display: block;
overflow: hidden;
border-radius: 4px;
background: white;
box-shadow: rgb(0 0 0 / 20%) 0px 3px 1px -2px,
rgb(0 0 0 / 14%) 0px 2px 2px 0px,
rgb(0 0 0 / 12%) 0px 1px 5px 0px;
}
.feed .header {
background-color: steelblue;
color: white;
padding: 0 .4em;
height: 31px;
line-height: 31px;
border-bottom: 1px solid gray;
}
.feed.noload .header {
background-color: darkgray;
}
.feed.error .header {
background-color: red;
}
.feed .header a,
.feed .header a:visited {
color: white;
}
/* line-indeterminate */
.line-indeterminate-root {
position: relative;
display: none;
margin: 0;
padding: 0;
height: 4px;
border: none;
background: steelblue;
}
.feed.loading .line-indeterminate-root {
display: block;
}
.line-indeterminate-root .line-indeterminate-bar {
position: absolute;
display: block;
height: 100%;
background: lightblue;
}
.line-indeterminate-root .line-indeterminate-bar.bar1 {
animation: 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) 0s infinite normal none running line-indeterminate-bar1;
transition: transform 0.2s linear 0s;
transform-origin: left center;
}
.line-indeterminate-root .line-indeterminate-bar.bar2 {
animation: 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) 1.15s infinite normal none running line-indeterminate-bar2;
transition: transform 0.2s linear 0s;
transform-origin: left center;
}
@keyframes line-indeterminate-bar1 {
0% {
right: 100%;
left: -35%;
}
60% {
right: -90%;
left: 100%;
}
100% {
right: -90%;
left: 100%;
}
}
@keyframes line-indeterminate-bar2 {
0% {
right: 100%;
left: -200%;
}
60% {
right: -8%;
left: 107%;
}
100% {
right: -8%;
left: 107%;
}
}
/**
* RSSフィードの要素 LOVE-G RSS-Readerの簡易版
* https://chrome.google.com/webstore/detail/love-g-rss-reader2/docpnbbbkcjpicndjcndadmnfgnhoicc?hl=ja
*/
class RssFeed {
constructor(topic, url) {
/**
* トピック
* @type {string}
*/
this.topic = topic;
/**
* RSSフィードのURL
* @type {string}
*/
this.url = url;
/**
* タグ/jQueryオブジェクト
* @type {jQuery}
*/
this.jTag = undefined;
}
/**
* フィードのタグを構築する
* DOMには接続していない
* @return {jQuery}
*/
createTag() {
const jTag = $(`
<li class="feed noload">
<div class="header">
<a class="title" target="_blank" rel="noopener" href="${this.url}">${this.topic} rss feed xml</a>
</div>
<div class="line-indeterminate-root">
<span class="line-indeterminate-bar bar1"></span>
<span class="line-indeterminate-bar bar2"></span>
</div>
<div class="items-root">
<ul class="items">
</ul>
</div>
</li>
`);
this.jTag = jTag;
return jTag;
}
load() {
this.jTag.removeClass('noload');
this.jTag.addClass('loading');
// 本来はajaxでRSSの記事を取得しにいくがCORSで取得できないので
// 3秒後取得できたことにする
setTimeout(() => {
this.jTag.removeClass('loading');
const jItems = this.jTag.find('.items');
jItems.empty();
const item = $(`
<li class="feed-item">
</li>
`);
item.text('読み込めた記事がここに載る');
item.appendTo(jItems);
}, 3000)
}
}
// トピックの配列
const topics = ['javascript', 'python', 'aws', 'typescript', 'react',
'flutter', 'docker', 'nextjs', 'go', 'ios', 'github',
'rails', 'php', 'nodejs', 'swift', 'linux', 'ruby',
'android', 'laravel', 'rust', 'firebase', 'unity',
'git', 'vuejs', 'css', 'dart', 'vscode', 'githubactions',
'csharp', 'macos', 'gcp', 'java', 'html', 'html5',
'windows', 'shopify', 'kubernetes', 'web', 'ポエム',
'機械学習', 'kotlin', 'azure', 'graphql', 'mysql', 'api',
'メモ', 'ubuntu', 'cpp', 'wailwindcss', 'test', 'terraform',
'lambda', '個人開発', 'vim', 'atcoder', 'switftui', 'xcode',
'raspberrypi', 'sql', 'nuxtjs', 'npm'
]
$(() => {
/**
* フィードを保持する配列
* @type {RssFeed[]}
*/
const feeds = []
// 監視オブジェクトを作成
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 表示された場合はRSSをロードして監視対象外とする
const elm = entry.target
const jElm = $(elm)
const topicNo = jElm.attr('data-topic-no')
// RssFeedのロードを呼び出す
feeds[topicNo].load()
// 監視の対象から外す
observer.unobserve(elm)
}
})
})
// 記事を保持するルート要素
const jContentRoot = $('.content-root')
// トピック分RSS記事を作成する
topics.forEach((topic, topicNo) => {
const url = `https://zenn.dev/topics/${topic}/feed`
const feed = new RssFeed(topic, url)
const jFeedTag = feed.createTag()
jFeedTag.appendTo(jContentRoot)
// 番号で逆引きできるように属性に配列番号を記録
jFeedTag.attr('data-topic-no', topicNo)
// フィードを格納しておく
feeds.push(feed)
// 監視対象へ
observer.observe(jFeedTag[0])
})
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.