<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])
  })
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.