| ClipArtMag Science Blog |

Free Cliparts

Hacker News Reader For No Reason - в 5K из Vanilla JS, HTML и CSS

Перевод статьи - A Hacker News Reader For No Reason - In 5K of Vanilla JS, HTML, & CSS

Автор - Mike Elliott

Источник оригинальной статьи:

https://elliotec.com/a-hacker-news-reader-for-no-reason/

Только наполовину названия верно.

Прошлой ночью я думал о том, как мой опыт полностью закрепился в React и современной экосистеме JavaScript в последнее время, возможно, повлиял на то, как я буду писать приложение вручную в vanilla HTML, CSS и JS без библиотек или чего-либо еще.

В тот же час я смотрел Hacker News и некоторые из его читателей, отмечая, как все они трудились до UX (в том числе HN - только в последнее время добавление комментария рушится и поддержка мобильных устройств), и обнаружил Hacker News API.

Ясно, что это дало возможность построить мой собственный HN reader самым легким способом я мог - полностью клиентская сторона, без библиотек, полностью vanilla.

Так что я сделал это в 2 файлы, 99 строк кода JavaScript и 99 строк HTML и CSS.

Файлы будут автоматически проходить через мой скрипт, так как они размещены на этом сайте (как описано здесь). В первую очередь JS составляет всего 4,4 КБ, но его сокращение и архивирование (gzipping) снизило его до 1,5 КБ, что довольно важно. HTML-файла составляет 3.5 КБ, минимизировано и архивированная.

Я сэкономлю вам арифметику, это в общей сложности 5 КБ на начальной нагрузке.

Перейти к A Hacker News Reader For No Reason!

HN App Image

Особенности

Ограниченные возможности

Поскольку я использую современные функции ECMAScript, только более современные браузеры будут поддерживать его, но большая часть моей аудитории, вероятно, в порядке с этим.

Это только показывает лучшие рассказов - нет поддержки New, Ask, Jobs и т. д. если они на первой странице. Это не ограничение для меня лично, так как я трачу 99% своего времени HN только на первой странице.

Нет входа, проголосовать за/против, или возможности публикации. К сожалению, HN API еще не поддерживает это, но если/когда это произойдет, возможно, я попытаюсь получить все это с помощью еще 99 строк: D

Я не дизайнер. Это было введено в моду с моим типичным астетическим минимальной темной темы с зеленым цветом повсюду. Наверное, не все любимые, но, конечно, мои, и это мои, так что неважно.

Код

Я написал стили в теге <style> в index.html. Поскольку одна из целей, которые я имел с этим, состояла в том, чтобы держать его супер маленьким и легким, я характерно установил произвольный предел для себя в числе, которое, как я считал, было разумным закончить его.

Я подделал некоторые из моих обычных правил стиля кода, и несколько раз я неуважительно относился к правилу линии 80-символов, которое я предпочитаю соблюдать, чтобы поразить метку строки 99 в обоих файлах, но ничего слишком сумасшедшего, и я не пересекаю никаких жестких строк плохой практики.

Вы можете проверить РЕПО здесь, но поскольку это всего 99 строк, я разместил содержимое файла JS ниже.

Обратите внимание на использование нескольких функций ES6+ и сходство с React и JSX с шаблонизацией. Конечно, здесь нет причудливого Virtual DOM или чего-то еще, и он просто манипулирует DOM по мере необходимости. Я использую функциональные и неизменяемые шаблоны, где это применимо, на языке нашего времени.



const hnBaseUrl = 'https://hacker-news.firebaseio.com/v0'
const state = {}
function fetchTopStories() {
  const topStoriesUrl = `${hnBaseUrl}/topstories.json`
  return fetch(topStoriesUrl).then(response => response.json())
    .then((data) => fetchStories(data))
}
function fetchStories(data) {
  const topStories = data.slice(0, 29)
  const storyIds = topStories.map((storyId) => {
    const storyUrl = `${hnBaseUrl}/item/${storyId}.json`
    return fetch(storyUrl).then((response) => response.json())
      .then((story) => story)
  })
  return Promise.all(storyIds).then((stories) => {
    state.stories = stories
    renderStories(stories)
  })
}
function renderStories(stories) {
  return stories.map((story) => {
    const userUrl = `https://news.ycombinator.com/user?id=${story.by}`
    const storyItemUrl = `https://news.ycombinator.com/item?id=${story.id}`
    const html = `
      <div class='story' id='${story.id}'>
        <h3 class='title'>
          ${story.url ? `<a href='${story.url}' target='_blank'>${story.title}</a>`
            : `<a href='javascript:void(0)' onclick="toggleStoryText('${story.id}')" >${story.title}</a>`}
        </h3>
        <span class='score'> ${story.score} </span> points by
        <a href='${userUrl}' target='_blank' class='story-by'> ${story.by}</a>
        <div class='toggle-view'>
          ${story.kids ? `
            <span
              onclick="fetchOrToggleComments('${story.kids}','${story.id}')"
              class='comments'
            > [toggle ${story.descendants} comments] </span>`
          : '' }
          <a href='${storyItemUrl}' target='_blank' class='hnLink'>[view on HN]</a>
        </div>
        ${story.text ?
          `<div class='storyText' id='storyText-${story.id}' style='display:none;'>
            ${story.text} </div>` : '' }
        <div id='comments-${story.id}' style='display: block;'></div>
      </div> `
    document.getElementById('hn').insertAdjacentHTML('beforeend', html)
  })
}
function toggleStoryText(storyId) {
  const storyText = document.getElementById(`storyText-${storyId}`)
  storyText.style.display = (storyText.style.display === 'block') ? 'none' : 'block'
}
function fetchComments(kids, storyId) {
  const commentIds = kids.split(',')
  const allComments = commentIds.map((commentId) => {
    const commentUrl = `${hnBaseUrl}/item/${commentId}.json`
    return fetch(commentUrl).then((response) => response.json()).then((comment) => comment)
  })
  return Promise.all(allComments).then((comments) => {
    state[storyId] = comments
    renderComments(comments, storyId)
  })
}
function fetchOrToggleComments(kids, storyId) {
  function toggleAllComments(storyId) {
    const allComments = document.getElementById(`comments-${storyId}`)
    allComments.style.display = (allComments.style.display === 'block') ? 'none' : 'block'
  }
  state[storyId] ? toggleAllComments(storyId) : fetchComments(kids, storyId)
}
function toggleComment(commentId) {
  const comment = document.getElementById(commentId)
  const toggle = document.getElementById(`toggle-${commentId}`)
  comment.style.display = (comment.style.display === 'block') ? 'none' : 'block'
  toggle.innerHTML = (toggle.innerHTML === '[ - ]') ? '[ + ]' : '[ - ]'
}
function renderComments(comments, storyId) {
  return comments.map((comment) => {
    const userUrl = `https://news.ycombinator.com/user?id=${comment.by}`
    const html = comment.deleted || comment.dead ? '' : `
      <div class='comment'>
        <span
          onclick='toggleComment("${comment.id}")'
          href='javascript:void(0)'
          id='toggle-${comment.id}'
          class='toggle-comment'
        >[ - ]</span>
        <a href='${userUrl}' class='comment-by'> ${comment.by}</a>
        <div id=${comment.id} class='comment-text' style='display:block;'>
          ${comment.text}
        </div>
      </div> `
    comment.parent == storyId ?
      document.getElementById(`comments-${storyId}`).insertAdjacentHTML('beforeend', html)
      : document.getElementById(comment.parent).insertAdjacentHTML('beforeend', html)
    if (comment.kids) return fetchComments(comment.kids.toString(), storyId)
  })
}
fetchTopStories()

Вот оно у вас есть. Может быть, кто-то еще также найдет причину использовать A Hacker News Reader For No Reason.

Free Lawn Mower Clipart

Smiley Face Throwing Up Clipart

Crying Faces Clipart

Lion Face Drawing Images

Confetti Picture

Disney Pocahontas Drawings

Free Smiley Face Clipart

Car Key Drawing

Star Destroyer Drawing

Accident Cartoon Clipart