You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JS实现新闻Feed实时搜索更新的技术问题求助

问题描述

我学JavaScript两周了,现在用Guardian API做新闻应用。目前能加载新闻图片、链接和标题,但实时搜索功能用不了。想要用户输入搜索内容时,页面能即时更新新闻Feed。控制台显示逻辑是对的,但页面上搜不到结果。

试过创建「搜索到的文章」新数组,太麻烦还没法实时更新Feed;也试过切换h1标题的可见性,一直报「not a function」错误。求可行的解决方案。


视图类(ArticlesView)

class ArticlesView {
  constructor(model, api) {
    this.model = model;
    this.api = api;
    this.articlesFromModel = this.model.getArticles();
    this.newsFeed = document.querySelector("#news-feed");
    this.clearFeedBtn = document.querySelector("#clear-feed-button");
    this.refreshBtn = document.querySelector("#refresh-button");
    this.searchInput = document.querySelector("#search-input");
    this.allHeadlines = [document.querySelectorAll("h1")];
    this.clearFeedBtn.addEventListener("click", () => {
      this.clearFeed();
    });
    this.searchInput.addEventListener("input", (e) => {
      this.articlesFromModel.forEach((article) => {
        const searchInput = e.target.value.toLowerCase();
        const isVisible = article.webTitle.toLowerCase().includes(searchInput);
        if (!isVisible) { 
          // 不知道这里该写什么
        }
        console.log(searchInput);
        console.log(article.webTitle);
        console.log(isVisible);
      });
    });
  }

  displayArticlesFromApi() {
    this.api.loadArticles(
      (repoData) => {
        this.model.addArticle(repoData.response.results);
        this.displayArticles();
      },
      () => {
        this.displayError();
      }
    );
  }

  displayArticles() {
    this.articlesFromModel.forEach((article) => {
      this.addImage(article);
      this.addHeadline(article);
    });
  }

  addHeadline(article) {
    const h1 = document.createElement("h1");
    h1.className = "news-title";
    h1.innerText = article.webTitle;
    h1.onclick = () => {
      window.location.href = article.webUrl;
    };
    this.newsFeed.append(h1);
  }

  addImage(article) {
    const img = document.createElement("img");
    img.className = "news-image";
    img.setAttribute("id", article.id);
    img.src = article.fields.thumbnail;
    img.onclick = () => {
      window.location.href = article.webUrl;
    };
    this.newsFeed.append(img);
  }

  displayError() {
    let errorMessage = document.createElement("div");
    errorMessage.className = "error";
    errorMessage.textContent = "Oops, something went wrong!";
    this.newFeed.append(errorMessage);
  }

  clearFeed() {
    const images = document.querySelectorAll("img.news-image");
    images.forEach((element) => {
      element.remove();
    });
    const headlines = document.querySelectorAll("h1.news-title");
    headlines.forEach((element) => {
      element.remove();
    });
  }
}

module.exports = ArticlesView;

模型类(ArticlesModel)

class ArticlesModel {
  constructor() {
    this.articles = [];
  }

  getArticles() {
    return this.articles;
  }

  addArticle(article) {
    article.forEach((a) => {
      this.articles.push(a);
    });
  }

  reset() {
    this.articles = [];
  }
}

module.exports = ArticlesModel;

API类(GuardianApi)

class GuardianApi {
  constructor() {
    this.apiURL = `https://content.guardianapis.com/search?api-key=${apikey}&show-fields=thumbnail`;
  }
  loadArticles(callback) {
    fetch(this.apiURL)
      .then((response) => response.json())
      .then((data) => {
        callback(data);
      });
  }
}

module.exports = GuardianApi;

入口文件(index.js)

console.log("the app is running");
const ArticlesModel = require("./src/ArticlesModel.js");
const ArticlesView = require("./src/ArticlesView.js");
const GuardianApi = require("./src/GuardianApi.js");

const client = new GuardianApi();
const model = new ArticlesModel();
const view = new ArticlesView(model, client);

view.displayArticlesFromApi();

HTML文件

<!doctype html>
<html>
   <head>
    <link rel="stylesheet" href="style.css">
      <title> Your Daily News Feed</title>
   </head>
   <body>
      <h1>News Feed</h1>

        <input type="search" id="search-input" placeholder="e.g. Football...">
      
      <br><br>
      <button onClick="window.location.reload();">Refresh Page</button> <button id="clear-feed-button">Clear Feed</button>
      
      <br> <br>

      <div id="news-feed">

      </div>
   </body>
   <script src="bundle.js"></script>

</html>

解决方案

核心问题分析

  1. 构造函数里的articlesFromModel是初始空数组的引用,API加载新文章后不会自动更新,导致搜索时拿不到最新数据
  2. 没有把文章数据和对应的DOM元素绑定,找不到要控制可见性的元素
  3. this.allHeadlines = [document.querySelectorAll("h1")];写法错误,把NodeList塞进数组,后续操作必然报错

具体修复步骤

1. 绑定文章数据与DOM元素

修改addHeadline和addImage方法,给元素加自定义属性关联文章ID:

addHeadline(article) {
  const h1 = document.createElement("h1");
  h1.className = "news-title";
  h1.innerText = article.webTitle;
  h1.dataset.articleId = article.id; // 绑定文章ID
  h1.onclick = () => {
    window.location.href = article.webUrl;
  };
  this.newsFeed.append(h1);
}

addImage(article) {
  const img = document.createElement("img");
  img.className = "news-image";
  img.dataset.articleId = article.id; // 绑定文章ID
  img.src = article.fields.thumbnail;
  img.onclick = () => {
    window.location.href = article.webUrl;
  };
  this.newsFeed.append(img);
}

2. 修复搜索逻辑

每次输入时从模型拿最新文章,通过文章ID匹配DOM元素并控制显示:

this.searchInput.addEventListener("input", (e) => {
  const searchTerm = e.target.value.toLowerCase();
  const allArticles = this.model.getArticles();
  
  allArticles.forEach(article => {
    const isMatch = article.webTitle.toLowerCase().includes(searchTerm);
    // 通过自定义属性找对应元素
    const headline = document.querySelector(`h1.news-title[data-article-id="${article.id}"]`);
    const image = document.querySelector(`img.news-image[data-article-id="${article.id}"]`);
    
    if (headline) headline.style.display = isMatch ? "block" : "none";
    if (image) image.style.display = isMatch ? "block" : "none";
  });
});

3. 确保获取最新文章数据

删掉构造函数里的this.articlesFromModel = this.model.getArticles();,每次需要文章时直接调用this.model.getArticles(),比如修改displayArticles:

displayArticles() {
  const allArticles = this.model.getArticles();
  allArticles.forEach((article) => {
    this.addImage(article);
    this.addHeadline(article);
  });
}

4. 修复拼写错误

displayError里的this.newFeed应为this.newsFeed(少了一个s)

额外优化

给搜索加防抖,避免输入时频繁触发事件:

let debounceTimer;
this.searchInput.addEventListener("input", (e) => {
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    // 上面的搜索逻辑放这里
  }, 300);
});

内容的提问来源于stack exchange,提问作者Tardigrade77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:35:20