├── README.md ├── index.html ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # WikiPedia 2 | This Is A Working Version Of WikiPedia Using It's API Key & With A Very Beautiful And Neat & Clean UI Like Google... 3 | 4 | # Demo 🦖 5 | https://pb2204.github.io/WikiPedia 6 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | WikiPedia 15 | 16 | 17 | 18 |
19 |

Search Me A Wikipedia Powered Search Engine

20 |
21 |

Search Term Entry

22 | 33 | 43 |
44 |
45 |

Search Results

46 |
47 |
48 |
49 |
50 |
51 |
52 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("readystatechange", (event) => { 2 | if (event.target.readyState === "complete") { 3 | initApp(); 4 | } 5 | }); 6 | 7 | const initApp = () => { 8 | setSearchFocus(); 9 | const search = document.getElementById("search"); 10 | search.addEventListener("input", showClearTextButton); 11 | const clear = document.getElementById("clear"); 12 | clear.addEventListener("click", clearSearchText); 13 | clear.addEventListener("keydown", clearPushListener); 14 | const form = document.getElementById("searchBar"); 15 | form.addEventListener("submit", submitTheSearch); 16 | }; 17 | 18 | // Procedural "workflow" function 19 | const submitTheSearch = (event) => { 20 | event.preventDefault(); 21 | deleteSearchResults(); 22 | processTheSearch(); 23 | setSearchFocus(); 24 | }; 25 | 26 | // Procedural 27 | const processTheSearch = async () => { 28 | clearStatsLine(); 29 | const searchTerm = getSearchTerm(); 30 | if (searchTerm === "") return; //TODO: 31 | const resultArray = await retrieveSearchResults(searchTerm); 32 | if (resultArray.length) buildSearchResults(resultArray); 33 | setStatsLine(resultArray.length); 34 | }; 35 | 36 | const setSearchFocus = () => { 37 | document.getElementById("search").focus(); 38 | }; 39 | 40 | const showClearTextButton = () => { 41 | const search = document.getElementById("search"); 42 | const clear = document.getElementById("clear"); 43 | if (search.value.length) { 44 | clear.classList.remove("none"); 45 | clear.classList.add("flex"); 46 | } else { 47 | clear.classList.add("none"); 48 | clear.classList.remove("flex"); 49 | } 50 | }; 51 | 52 | const clearSearchText = (event) => { 53 | event.preventDefault(); 54 | document.getElementById("search").value = ""; 55 | const clear = document.getElementById("clear"); 56 | clear.classList.add("none"); 57 | clear.classList.remove("flex"); 58 | setSearchFocus(); 59 | }; 60 | 61 | const clearPushListener = (event) => { 62 | if (event.key === "Enter" || event.key === " ") { 63 | event.preventDefault(); 64 | document.getElementById("clear").click(); 65 | } 66 | }; 67 | 68 | const deleteSearchResults = () => { 69 | const parentElement = document.getElementById("searchResults"); 70 | let child = parentElement.lastElementChild; 71 | while (child) { 72 | parentElement.removeChild(child); 73 | child = parentElement.lastElementChild; 74 | } 75 | }; 76 | 77 | const buildSearchResults = (resultArray) => { 78 | resultArray.forEach((result) => { 79 | const resultItem = createResultItem(result); 80 | const resultContents = document.createElement("div"); 81 | resultContents.classList.add("resultContents"); 82 | if (result.img) { 83 | const resultImage = createResultImage(result); 84 | resultContents.append(resultImage); 85 | } 86 | const resultText = createResultText(result); 87 | resultContents.append(resultText); 88 | resultItem.append(resultContents); 89 | const searchResults = document.getElementById("searchResults"); 90 | searchResults.append(resultItem); 91 | }); 92 | }; 93 | 94 | const createResultItem = (result) => { 95 | const resultItem = document.createElement("div"); 96 | resultItem.classList.add("resultItem"); 97 | const resultTitle = document.createElement("div"); 98 | resultTitle.classList.add("resultTitle"); 99 | const link = document.createElement("a"); 100 | link.href = `https://en.wikipedia.org/?curid=${result.id}`; 101 | link.textContent = result.title; 102 | link.target = "_blank"; 103 | resultTitle.append(link); 104 | resultItem.append(resultTitle); 105 | return resultItem; 106 | }; 107 | 108 | const createResultImage = (result) => { 109 | const resultImage = document.createElement("div"); 110 | resultImage.classList.add("resultImage"); 111 | const img = document.createElement("img"); 112 | img.src = result.img; 113 | img.alt = result.title; 114 | resultImage.append(img); 115 | return resultImage; 116 | }; 117 | 118 | const createResultText = (result) => { 119 | const resultText = document.createElement("div"); 120 | resultText.classList.add("resultText"); 121 | const resultDescription = document.createElement("p"); 122 | resultDescription.classList.add("resultDescription"); 123 | resultDescription.textContent = result.text; 124 | resultText.append(resultDescription); 125 | return resultText; 126 | }; 127 | 128 | const clearStatsLine = () => { 129 | document.getElementById("stats").textContent = ""; 130 | }; 131 | 132 | const setStatsLine = (numberOfResults) => { 133 | const statLine = document.getElementById("stats"); 134 | if (numberOfResults) { 135 | statLine.textContent = `Displaying ${numberOfResults} results.`; 136 | } else { 137 | statLine.textContent = "Sorry, no results."; 138 | } 139 | }; 140 | 141 | const getSearchTerm = () => { 142 | const rawSearchTerm = document.getElementById("search").value.trim(); 143 | const regex = /[ ]{2,}/gi; 144 | const searchTerm = rawSearchTerm.replaceAll(regex, " "); 145 | return searchTerm; 146 | }; 147 | 148 | const retrieveSearchResults = async (searchTerm) => { 149 | const wikiSearchString = getWikiSearchString(searchTerm); 150 | const wikiSearchResults = await requestData(wikiSearchString); 151 | let resultArray = []; 152 | if (wikiSearchResults.hasOwnProperty("query")) { 153 | resultArray = processWikiResults(wikiSearchResults.query.pages); 154 | } 155 | return resultArray; 156 | }; 157 | 158 | const getWikiSearchString = (searchTerm) => { 159 | const maxChars = getMaxChars(); 160 | const rawSearchString = `https://en.wikipedia.org/w/api.php?action=query&generator=search&gsrsearch=${searchTerm}&gsrlimit=20&prop=pageimages|extracts&exchars=${maxChars}&exintro&explaintext&exlimit=max&format=json&origin=*`; 161 | const searchString = encodeURI(rawSearchString); 162 | return searchString; 163 | }; 164 | 165 | const getMaxChars = () => { 166 | const width = window.innerWidth || document.body.clientWidth; 167 | let maxChars; 168 | if (width < 414) maxChars = 65; 169 | if (width >= 414 && width < 1400) maxChars = 100; 170 | if (width >= 1400) maxChars = 130; 171 | return maxChars; 172 | }; 173 | 174 | const requestData = async (searchString) => { 175 | try { 176 | const response = await fetch(searchString); 177 | const data = await response.json(); 178 | return data; 179 | } catch (err) { 180 | console.error(err); 181 | } 182 | }; 183 | 184 | const processWikiResults = (results) => { 185 | const resultArray = []; 186 | Object.keys(results).forEach((key) => { 187 | const id = key; 188 | const title = results[key].title; 189 | const text = results[key].extract; 190 | const img = results[key].hasOwnProperty("thumbnail") 191 | ? results[key].thumbnail.source 192 | : null; 193 | const item = { 194 | id: id, 195 | title: title, 196 | img: img, 197 | text: text, 198 | }; 199 | resultArray.push(item); 200 | }); 201 | return resultArray; 202 | }; -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | main, 2 | footer, 3 | .searchEntry, 4 | .results, 5 | .results .searchResults .resultItem { 6 | display: flex; 7 | align-items: center; 8 | } 9 | 10 | * { 11 | padding: 0; 12 | margin: 0; 13 | box-sizing: border-box; 14 | } 15 | 16 | html, 17 | body { 18 | background-color: #fff; 19 | color: #000; 20 | min-height: 100vh; 21 | font-family: "Roboto", Arial, sans-serif; 22 | font-size: 22px; 23 | } 24 | 25 | main { 26 | flex-direction: column; 27 | justify-content: flex-start; 28 | min-height: calc(100vh - 60px); 29 | } 30 | 31 | footer { 32 | width: 100%; 33 | height: 60px; 34 | justify-content: center; 35 | } 36 | 37 | footer p { 38 | color: #70757a; 39 | font-size: 0.5rem; 40 | } 41 | 42 | @media only screen and (min-width: 768px) { 43 | footer p { 44 | font-size: 1rem; 45 | } 46 | } 47 | 48 | footer p a { 49 | color: #70757a; 50 | } 51 | 52 | img { 53 | display: block; 54 | } 55 | 56 | .searchEntry { 57 | flex-direction: column; 58 | justify-content: flex-start; 59 | padding-top: 40px; 60 | } 61 | 62 | .searchEntry .logo { 63 | letter-spacing: -5px; 64 | font-size: 2rem; 65 | font-weight: 600; 66 | margin-bottom: 0.5rem; 67 | } 68 | 69 | @media only screen and (min-width: 768px) { 70 | .searchEntry .logo { 71 | font-size: 4rem; 72 | letter-spacing: -10px; 73 | } 74 | } 75 | 76 | .searchEntry .searchBar { 77 | width: 90vw; 78 | display: flex; 79 | border: 2px solid #e6e6e6; 80 | border-radius: 500px; 81 | padding: 0.15rem 0.25rem 0.15rem 0.75rem; 82 | } 83 | 84 | @media only screen and (min-width: 768px) { 85 | .searchEntry .searchBar { 86 | width: 80vw; 87 | padding: 1.25rem 1.5rem; 88 | } 89 | } 90 | 91 | @media only screen and (min-width: 1025px) { 92 | .searchEntry .searchBar { 93 | width: 60vw; 94 | } 95 | } 96 | 97 | .searchEntry .searchBar input[type="text"] { 98 | flex-grow: 1; 99 | font-size: 0.75rem; 100 | text-align: left; 101 | letter-spacing: 0.1rem; 102 | border: 0; 103 | outline: none; 104 | min-width: 150px; 105 | } 106 | 107 | @media only screen and (min-width: 768px) { 108 | .searchEntry .searchBar input[type="text"] { 109 | font-size: 1.25rem; 110 | } 111 | } 112 | 113 | .searchEntry .searchBar .button { 114 | cursor: pointer; 115 | border: 0; 116 | background: transparent; 117 | min-width: 48px; 118 | min-height: 48px; 119 | outline: none; 120 | } 121 | 122 | .searchEntry .searchBar .button i { 123 | font-family: "Font Awesome 5 Free"; 124 | font-size: 1rem; 125 | } 126 | 127 | @media only screen and (min-width: 768px) { 128 | .searchEntry .searchBar .button i { 129 | font-size: 1.5rem; 130 | } 131 | } 132 | 133 | .searchEntry .searchBar .button:hover i, 134 | .searchEntry .searchBar .button:focus i { 135 | padding-bottom: 0.5rem; 136 | border-bottom-width: 1px; 137 | border-bottom-style: solid; 138 | } 139 | 140 | .searchEntry .searchBar .searchButton:hover i, 141 | .searchEntry .searchBar .searchButton:focus i { 142 | color: #3cba54; 143 | } 144 | 145 | .searchEntry .searchBar .clear:hover i, 146 | .searchEntry .searchBar .clear:focus i { 147 | color: #db3236; 148 | } 149 | 150 | @media only screen and (min-width: 768px) { 151 | .searchEntry .searchBar .searchButton { 152 | padding-left: 1rem; 153 | } 154 | } 155 | 156 | .searchEntry .searchBar .clear { 157 | justify-content: center; 158 | align-items: center; 159 | color: #70757a; 160 | border-right: thin solid #d9d9d9; 161 | } 162 | 163 | @media only screen and (min-width: 768px) { 164 | .searchEntry .searchBar .clear { 165 | padding: 0 1rem; 166 | } 167 | } 168 | 169 | .searchEntry .searchBar:hover, 170 | .searchEntry .searchBar:focus-within { 171 | box-shadow: 0 2px 5px 2px #e6e6e6; 172 | } 173 | 174 | .results { 175 | flex-direction: column; 176 | justify-content: flex-start; 177 | padding: 0.5rem 1rem; 178 | width: 90vw; 179 | } 180 | 181 | @media only screen and (min-width: 768px) { 182 | .results { 183 | width: 75vw; 184 | } 185 | } 186 | 187 | @media only screen and (min-width: 1025px) { 188 | .results { 189 | width: 55vw; 190 | } 191 | } 192 | 193 | .results .statsBar { 194 | width: 100%; 195 | } 196 | 197 | .results .statsBar .stats { 198 | color: #70757a; 199 | font-size: 0.75rem; 200 | } 201 | 202 | @media only screen and (min-width: 768px) { 203 | .results .statsBar .stats { 204 | font-size: 1rem; 205 | } 206 | } 207 | 208 | .results .searchResults { 209 | width: 100%; 210 | } 211 | 212 | .results .searchResults .resultItem { 213 | flex-direction: column; 214 | justify-content: flex-start; 215 | width: 100%; 216 | padding: 0.5rem 0; 217 | border-bottom: 2px solid #dfdfdf; 218 | } 219 | 220 | @media only screen and (min-width: 768px) { 221 | .results .searchResults .resultItem { 222 | padding: 1.5rem 0; 223 | } 224 | } 225 | 226 | .results .searchResults .resultItem .resultTitle { 227 | width: 100%; 228 | text-align: left; 229 | font-size: 1rem; 230 | line-height: 1.5rem; 231 | margin-bottom: 0.25rem; 232 | overflow: hidden; 233 | white-space: nowrap; 234 | text-overflow: ellipsis; 235 | padding: 3px 2px; 236 | } 237 | 238 | @media only screen and (min-width: 768px) { 239 | .results .searchResults .resultItem .resultTitle { 240 | font-size: 1.5rem; 241 | line-height: 1.75rem; 242 | margin-bottom: 0.5rem; 243 | } 244 | } 245 | 246 | .results .searchResults .resultItem .resultTitle a { 247 | color: #1a0dab; 248 | text-decoration: none; 249 | cursor: pointer; 250 | } 251 | 252 | .results .searchResults .resultItem .resultTitle a:visited { 253 | color: #609; 254 | } 255 | 256 | .results .searchResults .resultItem .resultTitle a:hover { 257 | text-decoration: underline; 258 | } 259 | 260 | .results .searchResults .resultItem .resultTitle a:focus { 261 | outline: 2px solid #000; 262 | } 263 | 264 | .results .searchResults .resultItem .resultContents { 265 | display: flex; 266 | width: 100%; 267 | } 268 | 269 | .results .searchResults .resultItem .resultContents .resultImage { 270 | margin-right: 0.5rem; 271 | } 272 | 273 | .results .searchResults .resultItem .resultContents .resultText { 274 | flex-grow: 1; 275 | font-size: 14px; 276 | line-height: 20px; 277 | max-height: 60px; 278 | overflow: hidden; 279 | text-overflow: ellipsis; 280 | } 281 | 282 | @media only screen and (min-width: 768px) { 283 | .results .searchResults .resultItem .resultContents .resultText { 284 | font-size: 1rem; 285 | line-height: 28px; 286 | } 287 | } 288 | 289 | .blue { 290 | color: #4885ed; 291 | } 292 | 293 | .red { 294 | color: #db3236; 295 | } 296 | 297 | .yellow { 298 | color: #ffc107; 299 | } 300 | 301 | .green { 302 | color: #3cba54; 303 | } 304 | 305 | .exclaim { 306 | display: inline-block; 307 | font-size: 2.5rem; 308 | transform: rotate(12deg); 309 | } 310 | 311 | @media only screen and (min-width: 768px) { 312 | .exclaim { 313 | font-size: 5rem; 314 | } 315 | } 316 | 317 | .offscreen { 318 | position: absolute; 319 | left: -10000px; 320 | } 321 | 322 | .none { 323 | display: none; 324 | } 325 | 326 | .flex { 327 | display: flex; 328 | } --------------------------------------------------------------------------------