Last active
June 15, 2020 06:40
-
-
Save valk/cf87295c46eb136c9fdb9789c8eead8b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>Search</title> | |
<style> | |
body { margin: 0; box-sizing: border-box; } | |
.header { width: 100%; height: 36px; background: #DAE3ED; padding: 14px; color: #111; } | |
.search { margin: 0 40px; } | |
.search__input { height: 26px; width: 40%; border-radius: 4px; padding: 4px 8px; | |
font: 18px/22px Arial, Helvetica, sans-serif; border: 1px solid #bebebe; } | |
.search__input:focus { border: 1px solid #00f; } | |
.search__results { margin: 10px 0 0; width: 650px; background: #fff; box-shadow: 0 0 13px #d0d0d0; padding: 14px; } | |
.search__hidden { display: none; } | |
.search__section-content { display: flex; flex-direction: column; flex-wrap: wrap; align-items: baseline; | |
align-content: space-between; height: 210px; margin: 0 0 20px; } | |
.search__section-title { display: block; margin: 10px 0; color: #36526C; | |
font: bold 16px/22px Arial, Helvetica, sans-serif; } | |
.card { display: inline-block; width: 290px; height: 50px; overflow: hidden; margin: 10px; } | |
.card__image { float: left; width: 50px; height: 50px; margin: 0 20px 0 0; } | |
.card__text { color: #36526C; font: 18px/22px Arial, Helvetica, sans-serif; } | |
</style> | |
<script> | |
'use strict'; | |
(function(doc) { | |
const items = [ | |
{ id: 1, image: "https://images.folloze.com/image/upload/v1450949154/folloze-image-gallery/campaign/heroimage01.png", title: "Space Mobile The Final Frontier" }, | |
{ id: 2, image: "https://images.folloze.com/image/upload/v1450949153/folloze-image-gallery/campaign/heroimage13.png", title: "What if They Let You Run The Hubble Mobile" }, | |
{ id: 3, image: "https://images.folloze.com/image/upload/v1450949153/folloze-image-gallery/campaign/heroimage04.png", title: "Shooting Stars Mobile" }, | |
{ id: 4, image: "https://images.folloze.com/image/upload/v1450949149/folloze-image-gallery/campaign/heroimage11.png", title: "Make Money Online Through Advertising" }, | |
{ id: 5, image: "https://images.folloze.com/image/upload/v1450949143/folloze-image-gallery/campaign/heroimage08.png", title: "What Makes Flyers Unrivaled" }, | |
{ id: 6, image: "https://images.folloze.com/image/upload/v1450948643/folloze-image-gallery/campaign/heroimage05.png", title: "Adwords Keywork Research For Beginners" } | |
] | |
const boards = [ | |
{ id: 1, image: "https://images.folloze.com/image/upload/v1458832608/folloze-image-gallery/customer/customer_03.png", title: "The Baiscs Of Buying A Telescope Mobile" }, | |
{ id: 2, image: "https://images.folloze.com/image/upload/v1450949395/folloze-image-gallery/customer/customer_02.png", title: "The Universe Through A Child Eyes Mobile" }, | |
{ id: 3, image: "https://images.folloze.com/image/upload/v1450949032/folloze-image-gallery/customer/customer_04.png", title: "Home Business Advertising Ideas Mobile" }, | |
{ id: 4, image: "https://images.folloze.com/image/upload/v1450949131/folloze-image-gallery/group/group_01.png", title: "Finally A Top Mobile Way You Can Google Adwords Pay Per Clicks" }, | |
{ id: 5, image: "https://images.folloze.com/image/upload/v1450949052/folloze-image-gallery/group/group_02.png", title: "Study 800 Numbers Still Popula With Advertisers Mobile" }, | |
{ id: 6, image: "https://images.folloze.com/image/upload/v1450948179/folloze-image-gallery/generic/generic_06.png", title: "Using Banner Stands To Increase Trade Show Traffic" } | |
] | |
// This is a generic search structure with a simple substring search. | |
// | |
function Search(data) { | |
// Prepare lowercaseTitles for faster search | |
this.data = data.map(function(item) { | |
item.lowercaseTitle = item.title.toLowerCase(); | |
return item; | |
}); | |
// This actually does the search. And it memoizes the results in a hash, so the callers | |
// won't worry about performance | |
this.containing = function(substr) { | |
substr = substr.trim(); | |
this.containingMemo = this.containingMemo || {}; | |
// Note, for better performance we'll limit this memo to say 100, and will add appropriate code | |
// omitting that because it's not relevant for this test. | |
if (this.containingMemo[substr]) { | |
return this.containingMemo[substr]; | |
} | |
if (!substr || substr === '') { | |
return this.data; | |
} | |
this.containingMemo[substr] = this.data.filter(function(item) { | |
return item.lowercaseTitle.indexOf(substr.toLowerCase()) > -1; | |
}); | |
return this.containingMemo[substr]; | |
} | |
this.countResults = function(substr) { | |
substr = substr.trim(); | |
return this.containing(substr).length; | |
} | |
this.buildSearchResults = function(substr) { | |
substr = substr.trim(); | |
const items = this.containing(substr); | |
let itemsHTML = ''; | |
items.forEach(function (itemObj) { | |
itemsHTML += | |
'<div class="card" data-item-id="' + itemObj.id + '">' + | |
' <img class="card__image" src="' + itemObj.image + '" />' + | |
' <div class="card__text js-card-text">' + | |
itemObj.title + | |
' </div>' + | |
'</div>'; | |
}); | |
return itemsHTML; | |
} | |
} | |
function limitTextH(sEls, maxHeight) { | |
sEls.forEach(function (p) { | |
let text = p.textContent, | |
textLen = text.length; | |
while (textLen-- && p.offsetHeight > maxHeight) { | |
p.textContent = text.substring(0, textLen) + '…'; | |
} | |
}); | |
} | |
function toggleSearchResultsVisibility(substr) { | |
const sResults = document.querySelector('.search__results'); | |
if (!substr || substr.trim() === '') { | |
sResults.classList.add('search__hidden'); | |
return; | |
} | |
sResults.classList.remove('search__hidden'); | |
} | |
// Using DOMContentLoaded because the test question said to write the code here. Otherwise it'd | |
// wiser in this case just to add the script after HTML without the event. | |
// | |
doc.addEventListener("DOMContentLoaded", function() { | |
const itemsSearch = new Search(items); | |
const boardsSearch = new Search(boards); | |
// Listen to key presses and render the searched results | |
const sSearchInput = document.querySelector('.search__input'); | |
function updateSearchResults(input) { | |
const sSections = doc.querySelector('.js-content-items'); | |
const sBoards = doc.querySelector('.js-content-boards'); | |
const sTitleItems = doc.querySelector('.js-results-title-items'); | |
const sTitleBoards = doc.querySelector('.js-results-title-boards'); | |
toggleSearchResultsVisibility(input); | |
// Note, innerHTML is slower but trades faster code delivery | |
sSections.innerHTML = itemsSearch.buildSearchResults(input); | |
sTitleItems.innerHTML = "Items (" + itemsSearch.countResults(input) + ')'; | |
sBoards.innerHTML = boardsSearch.buildSearchResults(input); | |
sTitleBoards.innerHTML = "Boards (" + boardsSearch.countResults(input) + ')'; | |
const sItems = document.querySelectorAll('.js-card-text'); | |
limitTextH(sItems, 64); | |
} | |
sSearchInput.addEventListener('keyup', function(e) { | |
const input = e.currentTarget.value; | |
updateSearchResults(input); | |
}); | |
}); | |
// Ok, at this point of time I'm pretty much happy with this code. | |
// Minor things I'd improve, but just don't see any motivation right now: | |
// * Make the search self-contained, means when we do new Search(..) we'd pass there also the selectors, | |
// and move into Search class the methods like toggleSearchResultsVisibility() and limitTextH(), in order | |
// to create reusable objects independent from the surrounding code. | |
// * Make the search results collapse heights when there are fewer items | |
// * Maybe reduce the whole section entirely where theres (0) results | |
// * Do something with the huge images to reduce the download bandwidth | |
// * Write tests (I manually tested on recent Chrome and FF browsers) | |
// But overall it's just time VS quality as usual. | |
}(document)); | |
</script> | |
</head> | |
<body> | |
<div class="header"> | |
<div class="search"> | |
<input class="search__input" type="text" placeholder="Type & joy..." /> | |
<div class="search__results search__hidden"> | |
<div class="search__section search__section--items"> | |
<strong class="search__section-title js-results-title-items">Items ()</strong> | |
<div class="search__section-content js-content-items"></div> | |
</div> | |
<div class="search__section search__section--boards"> | |
<strong class="search__section-title js-results-title-boards">Boards ()</strong> | |
<div class="search__section-content js-content-boards"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment