用于无限滚动的Intersection Observer API

时间:2020-08-24 02:18:36

标签: javascript api dom intersection-observer

我正在做一个抽搐Web应用程序。我尝试使用Intersection观察器API来实现无限滚动,并且它可以工作。但是,我注意到当无限滚动工作时,导航栏完全消失了。我猜问题是由“ DOMContentLoaded”引起的,但不确定。谢谢。

这是我的代码笔链接:Twitch

sendAsyncResult64()
const clientID = '5npghe3kytuifte3z9kvwnto50mqch';
const req = new XMLHttpRequest();

function showError() {
  alert('Error');
}

function getResp(url, callback) {
  req.open('GET', url, true);
  req.setRequestHeader('Client-ID', clientID);
  req.setRequestHeader('Accept', 'application/vnd.twitchtv.v5+json');
  req.send();
  req.onload = function () {
    if (req.status >= 200 && req.status < 400) {
      let data
      try {
        data = JSON.parse(req.response)
      } catch (err) {
        showError();
        return;
      }
      callback(data)
    } else {
      showError();
    }
  }
}

const navList = document.querySelector('.nav__list')
const streamBox = document.querySelector('.stream_box')
const streamItems = document.querySelector('.streamItems')
const langFilter = document.querySelector('.langFilter')
const langArr = ['ALL', 'EN', 'ZH', 'ES', 'FR', 'DE', 'RU', 'KO', 'JA', 'PT', 'AR'];
const urlRoot = 'https://api.twitch.tv/kraken/'
const topGameurl = `${urlRoot}games/top?limit=5`
const streamApi = `${urlRoot}streams/`
let offset = 0


document.addEventListener('DOMContentLoaded', ()=> {
  const target = document.querySelector('.stream-end')
  let options = {
    root: null,
    rootMargin: '30px', // looking entire viewport
    threshold: 0.5, // if 50% of footer
  }
  const observer = new IntersectionObserver(handleIntersection, options)
  observer.observe(target)
})

function handleIntersection(entries) {
  if (entries[0].isIntersecting) {
    let gameTitle = document.querySelector('.gameTitle')
    let gameURLname = encodeURIComponent(gameTitle.innerHTML)
    const loadmorestreamUrl = createURL(streamApi, gameURLname, offset)
    offset += 100
    getData(loadmorestreamUrl)
  }
}


function createURL(url, game, offset) {
  const streamUrl = `${url}?game=${game}&limit=20&offset=${offset}`
  return streamUrl
}

getResp(topGameurl, (data) => {
  const topGames = [...data.top]
  const result = topGames.reduce((result, item) => {
    result += `<li>${item.game.name}</li>`
    return result
  }, '')
  navList.innerHTML = result
  const gameName = encodeURIComponent(data.top[0].game.name)
  const streamUrl = createURL(streamApi, gameName, offset)
  getData(streamUrl)
})

navList.addEventListener('click', e => {
  streamItems.innerHTML = ''
  let gameTitle = document.querySelector('.gameTitle')
  const gameName = e.target.innerHTML
  gameTitle.innerHTML = gameName
  const gameNameURL = encodeURIComponent(gameName)
  const streamUrl = createURL(streamApi, gameNameURL, offset)
  getData(streamUrl)
})


function getData(url) {
  getResp(url, (data) => {
    const dataArrs = [...data.streams]
    dataArrs.map(dataArr => {
      let streamItem = document.createElement('div')
      streamItem.classList.add('stream')
      streamItem.innerHTML = `
      <p class="viewers">Viewers: ${dataArr.viewers}</p>
      <img src="${dataArr.preview.large}" alt="" class="preview">
      <div class="streamer">
        <img src="${dataArr.channel.logo}" alt="" class="logo">
        <p class="name">${dataArr.channel.name}</p>
        <p class="lang">${dataArr.channel.broadcaster_language.toUpperCase()}</p>
      </div>
    `
      streamItems.appendChild(streamItem)
    })
  })
}
html, body {
  font-size: 16px;
  box-sizing: border-box;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  background:black;
  background-attachment: fixed;
  
}

.container {
  width: 90%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  padding: 2rem;
  overflow-x: hidden;
}

#header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #fff;
  margin-bottom: 1.2rem;
}

.navbar {
  position: relative;
  transform: translateX(0%);
}

.title {
  margin-right: 1rem;
  font-size: 2rem;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-weight: 700;
  color: #741cf7;
}

.nav__list {
  display: flex;
}

.nav__list li {
  padding: .8rem .6rem;
  cursor: pointer;
  font-size: 1rem;
  font-weight: 700;
}

.nav__list li:hover {
  border-radius: .5rem;
  background: #a87ceb;
  transition: background .3s ease;
}

.nav__list li + li {
  margin-left: 1rem;
}

#stream_box {
  display: flex;
  flex-direction: column;
  border-radius: 8px;
  align-items: center;
  padding: 1.5rem;
  color: #fff;
  position: relative;
}

.game__title {
  font-size: 2rem;
  margin-bottom: 1rem;
  font-weight: 600;
  color:#741cf7;
}

.top__twenty {
  font-size: 1.4rem;
  margin-bottom: 1.5rem;
}

.streamItems {
  display: flex;
  flex-flow: row wrap;
  width: 100%;
  justify-content: center;
}

.lang__options {
  position: absolute;
  top: 2rem;
  right: 7rem;
}

.lang__title {
  font-size: 1.2rem;
  color: #fff;
}

#language {
  outline: none;
  width: 3rem;
  background: transparent;
  color: #741cf7;
  border: none;
  font-size: 1rem;
}

.stream {
  width: 30%;
  margin: 1.5rem;
  background-color: rgba(255, 255, 255, .15);
  backdrop-filter: blur(5px);
  cursor: pointer;
}

.preview {
  width: 100%;
  vertical-align: middle;
}

.viewers {
  display: none;
}

.stream:hover > .viewers {
  display: block;
  position: absolute;
  z-index: -1;
  animation-name: move;
  animation-duration: .4s;
  animation-timing-function: ease;
  animation-fill-mode: forwards;
}

@keyframes move {
  from {
    top: 0px;
  }
  to {
    top: -20px;
  }
}

.streamer {
  display: flex;
  align-items: center;
  padding: .5rem;
  position: relative;
  color: #fff;
}

.logo {
  width: 15%;
  border-radius: 50%;
  margin-right: .8rem;
}

.lang {
  position: absolute;
  right: .5rem;
  bottom: .5rem;
}

.hidden {
  display: none;
}

.check {
  display: none;
}

.rwdSwitch {
  display: none;
}

.gameTitle {
  color: #fff;
}


@media screen and (max-width: 1024px) {
  .lang__options {
    position: absolute;
    top: 7rem;
    right: 50%;
    transform: translateX(50%);
  }

  .streamItems {
    flex-flow: row wrap;
    width: 100%;
    justify-content: center;
    margin-top: 1.2rem;
  }

  .stream {
    width: 100%;
  }

  .nav__list li {
    text-align: center;
    font-size: 1rem;
  }
}

@media screen and (max-width: 576px) {
  body {
    overflow-x: hidden;
  }
  .navbar {
    position: absolute;
    top: 20%;
    right: 50%;
    transform: translateX(200%);
    transition: transform .3s ease-in-out;
    background-color: rgba(255, 255, 255, .15);
    backdrop-filter: blur(5px);
    z-index: 999;
    visibility: hidden;
  }

  .top__twenty {
    font-size: .8rem;
  }

  .nav__list {
    flex-direction: column;
    align-items: flex-end;
  }

  .rwdSwitch {
    display: block;
    cursor: pointer;
    position: absolute;
    right:1.5rem;
    top: 2.5rem;
    z-index: 999;
  }

  .check:checked ~ .navbar {
    visibility: visible;
    transform: translateX(100%);
    transition: transform .3s ease-in-out;
  }

  
}

1 个答案:

答案 0 :(得分:1)

首先,请不要使用XMLHttpRequest,它已经很老了,难以使用和阅读。请改用fetch

您的问题是由您创建一个XMLHttpRequest引起的。因此,在第一个请求完成之前,第二个请求开始并取消第一个请求。只需将const req = new XMLHttpRequest();移至getResp

但是我只是将getResp更改为:

function getResp(url, callback) {
  fetch(url, {
    headers: {
      'Client-ID': clientID,
      'Accept': 'application/vnd.twitchtv.v5+json'
    }
  })
  .then(response => response.json())
  .then(callback)
  .catch(showError)
}