Skip to content

Instantly share code, notes, and snippets.

@valk
Last active June 15, 2020 06:40
Show Gist options
  • Save valk/cf87295c46eb136c9fdb9789c8eead8b to your computer and use it in GitHub Desktop.
Save valk/cf87295c46eb136c9fdb9789c8eead8b to your computer and use it in GitHub Desktop.
<!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