-
Notifications
You must be signed in to change notification settings - Fork 34
Exercise #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Exercise #19
Changes from all commits
cf560da
e960600
00e43ff
ff9f1a5
01728a5
e77414c
228ad53
3f3150b
444bd87
3a00ce2
5d354fc
849c0e5
9c3437e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta http-equiv="X-UA-Compatible" content="ie=edge"> | ||
| <link rel="stylesheet" type="text/css" href="styles/styles.css"> | ||
|
|
||
|
|
||
| <link href="https://fonts.googleapis.com/css?family=Source+Serif+Pro:700" rel="stylesheet"> | ||
| <title>Responsive News Reader</title> | ||
| </head> | ||
|
|
||
| <body> | ||
| <header class="header"> | ||
| <h2 class="header-title">Fetch News</h2> | ||
| <button class="country-button">Top UK Stories</button> | ||
|
|
||
| <form class="search-form"> | ||
| <input type="text" class="search-text" /> | ||
| <select name="date-range" class="date-range"> | ||
| <option value="0">Today</option> | ||
| <option value="1">Last Month</option> | ||
| <option value="12">Last Year</option> | ||
| <option value="all-time">All Time</option> | ||
| </select> | ||
| <button type="submit">Search</button> | ||
| </form> | ||
| <button class="advanced-search">Advanced Search</button> | ||
|
|
||
| </header> | ||
| <div class="content"> | ||
| <div class="advanced-search-options"> | ||
| <form class="advanced-search-form"> | ||
|
|
||
| <h6>Sort results by:</h6> | ||
| <select class="sort-by-select"> | ||
| <option value="relevancy">Most Relevant</option> | ||
| <option value="popularity">Most Popular</option> | ||
| <option value="publishedAt">Newest stories</option> | ||
| </select> | ||
|
|
||
|
|
||
|
|
||
| <select name="language-select" class="language-select"> | ||
| <option value="en">English</option> | ||
| <option value="fr">French</option> | ||
| <option value="de">German</option> | ||
| <option value="es">Spanish</option> | ||
| </select> | ||
|
|
||
| <input type="submit" /> | ||
| </form> | ||
| </div> | ||
| <div class="news-display"> | ||
|
|
||
| </div> | ||
|
|
||
|
|
||
| <div class="pagination"> | ||
| <button class="back-page" href="#">«</button> | ||
| <button class="next-page" value="next" href="#">»</button> | ||
| </div> | ||
| </div> | ||
|
|
||
|
|
||
| <script src="src/scripts.js"></script> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| // News API key: 9ed005ef4eb94baf913fce701c69972f | ||
|
|
||
| //TODO: stylization, excludeDomains/blocklist | ||
|
|
||
| //Object contains possible URLS for fetch requests | ||
| const apiRequests = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nice. I like how you have created an object that encapsulates all URL generation logic in one place |
||
| usTop20: 'https://newsapi.org/v2/top-headlines?country=us&apiKey=9ed005ef4eb94baf913fce701c69972f', | ||
|
|
||
| ukTop20: 'https://newsapi.org/v2/top-headlines?country=gb&apiKey=9ed005ef4eb94baf913fce701c69972f', | ||
|
|
||
|
|
||
| //object contains stored parameters for searches | ||
| customParameters : { | ||
| q: {val: null, string: ""}, | ||
| from: {val: null, string: ""}, | ||
| excludeDomains: {val: null, string: ""}, | ||
| language: {val: null, string: ""}, | ||
| page: {val: 1, string: ""}, | ||
| sortBy: {val: null, string: ""} | ||
| }, | ||
|
|
||
|
|
||
| //getURL provides a url for API with specified parameters above | ||
| getURL: function(){ | ||
| const customURL = `https://newsapi.org/v2/everything?${this.customParameters.q.string}${this.customParameters.from.string}${this.customParameters.excludeDomains.string}${this.customParameters.page.string}${this.customParameters.language.string}${this.customParameters.sortBy.string}&apiKey=9ed005ef4eb94baf913fce701c69972f` | ||
| return customURL | ||
| }, | ||
|
|
||
| updateURL: function(parameter, update){ | ||
| this.customParameters[parameter].val = update | ||
| if (parameter === "q"){ | ||
| this.customParameters[parameter].string = `${parameter}=${update}` | ||
| }else{ | ||
| this.customParameters[parameter].string = `&${parameter}=${update}` | ||
| } | ||
| }, | ||
|
|
||
|
|
||
| blockList: [] | ||
|
|
||
| } | ||
|
|
||
|
|
||
| //Object contains functions attached to buttons and page features | ||
| const pageHandlers = { | ||
|
|
||
| changeCountry: function(){ | ||
| const countryButtonElement = document.querySelector(".country-button") | ||
| countryButtonElement.addEventListener("click", event => { | ||
| paginationElement.style.display = "none"; | ||
| countryButtonElement.textContent === "Top UK Stories" | ||
| ? ( countryButtonElement.textContent = "Top US Stories", | ||
| fetchNews(apiRequests.ukTop20)) | ||
| : (countryButtonElement.textContent = "Top UK Stories", | ||
| fetchNews(apiRequests.usTop20)) | ||
| }) | ||
| }, | ||
|
|
||
| search: function(event){ | ||
| event.preventDefault(); | ||
| if (searchTextElement.value === ""){ | ||
| return | ||
| } | ||
| const dateRange = formatDate(dateRangeElement.value) | ||
| paginationElement.style.display = "inline" | ||
| apiRequests.updateURL("q", searchTextElement.value) | ||
| apiRequests.updateURL("from", dateRange) | ||
| fetchNews(apiRequests.getURL()) | ||
| searchTextElement.value = "" | ||
| pageHandlers.checkPage() | ||
| }, | ||
|
|
||
|
|
||
| checkPage: function(){ | ||
| apiRequests.customParameters.page.val === 1 | ||
| ? prevPageElement.disabled = true | ||
| : prevPageElement.disabled = false | ||
| }, | ||
|
|
||
|
|
||
| paginationControl: function(event){ | ||
| let currentPage = apiRequests.customParameters.page.val | ||
| event.target.value === "next" | ||
| ? currentPage ++ | ||
| : currentPage -- | ||
| apiRequests.updateURL("page", currentPage) | ||
| fetchNews(apiRequests.getURL()) | ||
| pageHandlers.checkPage() | ||
|
|
||
|
|
||
| }, | ||
|
|
||
| updateAdvancedOptions: event => { | ||
| event.preventDefault() | ||
| apiRequests.updateURL("language", languageSelector.value) | ||
| apiRequests.updateURL("sortBy", sortBySelector.value ) | ||
| pageHandlers.search(event) | ||
| }, | ||
|
|
||
| openCloseAdvanced: event => { | ||
| advancedSearchMenu.style.display === "none" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could rewritten slightly cleaner as |
||
| ? advancedSearchMenu.style.display = "block" | ||
| : advancedSearchMenu.style.display = "none" | ||
| }, | ||
|
|
||
| blockSource: event => { | ||
| const host = linkElement.hostname | ||
| console.log(host) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| function assignElement(elementType, elementClassName, elementTextContent){ | ||
| const output = document.createElement(elementType) | ||
| output.className = elementClassName | ||
| output.textContent = elementTextContent | ||
| return output | ||
| } | ||
|
|
||
|
|
||
| //creates each story panel from fetch response | ||
| function createStoryPanel(article){ | ||
| const storyDivElement = assignElement("div", "story-div-element") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic here would be easier to implement as an HTML string template |
||
| const headerElement = assignElement("div", "story-header") | ||
| const headerLeftElement = assignElement("div", "story-header-left") | ||
| const publicationInfoElement = assignElement("span", "publication-info") | ||
| const headlineElement = assignElement("h2", "headline", article.title) | ||
| const publicationNameElement = assignElement("h4", "publication-name", article.source.name) | ||
| const publicationDateElement = assignElement("h4", "publication-date", convertDateForDisplay(article.publishedAt)) | ||
| const blockButtonElement = assignElement("button", "block-button", "Block") | ||
| const storyImageElement = assignElement("img", "story-image") | ||
| storyImageElement.setAttribute("src", article.urlToImage) | ||
| if (storyImageElement.src === "http://127.0.0.1:3000/null"){ | ||
| storyImageElement.setAttribute("src", "../images/No_Image_Available.jpg") | ||
| } | ||
| const descriptionElement = assignElement("p", "description-element", article.description) | ||
| const linkElement = assignElement("a", "full-story-link", "See full story") | ||
| linkElement.setAttribute("href", article.url) | ||
| blockButtonElement.addEventListener("click", event => { | ||
| let host = linkElement.hostname | ||
| host = host.replace("www.", "") | ||
| apiRequests.blockList.push(host) | ||
| const blockedDomains = apiRequests.blockList.join(",") | ||
| apiRequests.updateURL("excludeDomains", blockedDomains) | ||
| fetchNews(apiRequests.getURL()) | ||
| }) | ||
|
|
||
| publicationInfoElement.appendChild(publicationNameElement) | ||
| publicationInfoElement.appendChild(publicationDateElement) | ||
|
|
||
| headerLeftElement.appendChild(headlineElement) | ||
| headerLeftElement.appendChild(publicationInfoElement) | ||
| headerLeftElement.appendChild(blockButtonElement) | ||
|
|
||
| headerElement.appendChild(headerLeftElement) | ||
| headerElement.appendChild(storyImageElement) | ||
|
|
||
| storyDivElement.appendChild(headerElement) | ||
| storyDivElement.appendChild(descriptionElement) | ||
| storyDivElement.appendChild(linkElement) | ||
| newsDisplayElement.appendChild(storyDivElement) | ||
| } | ||
|
|
||
|
|
||
|
|
||
| //formats date for search | ||
| function formatDate(monthsAgo){ | ||
| if ( monthsAgo != "all-time") { | ||
| const newDate = new Date | ||
| newDate.setMonth(newDate.getMonth() - monthsAgo); | ||
| const isoDate = newDate.toISOString().slice(0,10) | ||
| return `${isoDate}` | ||
| }else{ | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| //formats date for display | ||
|
|
||
| function convertDateForDisplay(date){ | ||
| const inputDate = new Date(date) | ||
| return inputDate.toLocaleString() | ||
| } | ||
|
|
||
|
|
||
| function fetchNews(apiAddress){ | ||
| fetch(apiAddress) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice. good to see how you pass in url as param and implement display logic in own function |
||
| .then(response => { | ||
| return response.json() | ||
| }) | ||
| .then(body => { | ||
| newsDisplayElement.innerHTML = "" | ||
| body.articles.forEach(article => { | ||
| createStoryPanel(article) | ||
| }) | ||
| }) | ||
| } | ||
|
|
||
|
|
||
| const contentElement = document.querySelector(".content") | ||
| const newsDisplayElement = document.querySelector(".news-display") | ||
| const paginationElement = document.querySelector(".pagination") | ||
| const nextPageElement = document.querySelector(".next-page") | ||
| const prevPageElement = document.querySelector(".back-page") | ||
| const searchFormElement = document.querySelector(".search-form") | ||
| const searchTextElement = document.querySelector(".search-text") | ||
| const dateRangeElement = document.querySelector(".date-range") | ||
| const advancedSearchElement = document.querySelector(".advanced-search") | ||
| const advancedSearchMenu = document.querySelector(".advanced-search-options") | ||
| const advancedSearchForm = document.querySelector(".advanced-search-form") | ||
| const languageSelector = document.querySelector(".language-select") | ||
| const sortBySelector = document.querySelector(".sort-by-select") | ||
| searchFormElement.addEventListener("submit", pageHandlers.search) | ||
| prevPageElement.addEventListener("click", pageHandlers.paginationControl) | ||
| nextPageElement.addEventListener("click", pageHandlers.paginationControl) | ||
| advancedSearchElement.addEventListener("click", pageHandlers.openCloseAdvanced) | ||
| advancedSearchForm.addEventListener("submit", pageHandlers.updateAdvancedOptions) | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| fetchNews(apiRequests.usTop20) | ||
| pageHandlers.changeCountry() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to have indentation used here. It will make the HTML structure easier to read