|
| 1 | +(function () { |
| 2 | + const searchForm = document.querySelector('#search'); |
| 3 | + |
| 4 | + searchForm.addEventListener('submit', event => { |
| 5 | + event.preventDefault(); |
| 6 | + |
| 7 | + updateOffers(); |
| 8 | + }); |
| 9 | + |
| 10 | + const obs = new MutationObserver(function (mutations) { |
| 11 | + console.log(mutations); |
| 12 | + }); |
| 13 | + |
| 14 | + obs.observe(document.documentElement, { |
| 15 | + attributes: true, |
| 16 | + attributeOldValue: true, |
| 17 | + characterData: true, |
| 18 | + characterDataOldValue: true, |
| 19 | + childList: true, |
| 20 | + subtree: true, |
| 21 | + }); |
| 22 | +})(); |
| 23 | + |
| 24 | +function updateOffers() { |
| 25 | + const list = document.querySelector('.result-list'); |
| 26 | + |
| 27 | + // Clear out existing children |
| 28 | + for (let el of list.children) { |
| 29 | + list.removeChild(el); |
| 30 | + } |
| 31 | + |
| 32 | + // Add new children |
| 33 | + // Allow to define children count via URL ?count=100 |
| 34 | + const url = new URL(window.location.href); |
| 35 | + const count = parseInt(url.searchParams.get('count') || 50); |
| 36 | + for (let i = 0; i < count; i++) { |
| 37 | + const el = document.createElement('div'); |
| 38 | + el.classList.add('result'); |
| 39 | + el.innerHTML = generateResult(); |
| 40 | + |
| 41 | + const id = crypto.randomUUID(); |
| 42 | + el.setAttribute('id', id); |
| 43 | + |
| 44 | + addListeners(id, el); |
| 45 | + |
| 46 | + list.appendChild(el); |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +function addListeners(id, el) { |
| 51 | + el.querySelector('[data-long-text-open]').addEventListener('click', event => { |
| 52 | + const parent = event.target.closest('.long-text'); |
| 53 | + parent.setAttribute('data-show-long', ''); |
| 54 | + }); |
| 55 | + el.querySelector('[data-long-text-close]').addEventListener('click', event => { |
| 56 | + const parent = event.target.closest('.long-text'); |
| 57 | + parent.removeAttribute('data-show-long'); |
| 58 | + }); |
| 59 | + |
| 60 | + // These are purposefully inefficient |
| 61 | + el.querySelector('[data-select]').addEventListener('click', () => { |
| 62 | + document.querySelectorAll('.result').forEach(result => { |
| 63 | + if (result.getAttribute('id') === id) { |
| 64 | + result.setAttribute('data-show-options', 'yes'); |
| 65 | + } else { |
| 66 | + result.setAttribute('data-show-options', 'no'); |
| 67 | + } |
| 68 | + }); |
| 69 | + |
| 70 | + // Do some more, extra expensive work |
| 71 | + document.querySelectorAll('.select__price').forEach(el => { |
| 72 | + el.setAttribute('js-is-checked', new Date().toISOString()); |
| 73 | + el.setAttribute('js-is-checked-2', new Date().toISOString()); |
| 74 | + el.setAttribute('js-is-checked-3', 'yes'); |
| 75 | + el.setAttribute('js-is-checked-4', 'yes'); |
| 76 | + el.setAttribute('js-is-checked-5', 'yes'); |
| 77 | + el.setAttribute('js-is-checked-6', 'yes'); |
| 78 | + }); |
| 79 | + document.querySelectorAll('.tag').forEach(el => el.setAttribute('js-is-checked', 'yes')); |
| 80 | + document.querySelectorAll('h3').forEach(el => el.setAttribute('js-is-checked', 'yes')); |
| 81 | + }); |
| 82 | +} |
| 83 | + |
| 84 | +const baseTitles = ['Cottage house', 'Cabin', 'Villa', 'House', 'Appartment', 'Cosy appartment']; |
| 85 | +const baseBeds = ['2', '2+2', '4+2', '6+2', '6+4']; |
| 86 | +const baseDescription = |
| 87 | + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; |
| 88 | + |
| 89 | +function generateResult() { |
| 90 | + const title = `${getRandomItem(baseTitles)} ${Math.ceil(Math.random() * 20)}`; |
| 91 | + const beds = getRandomItem(baseBeds); |
| 92 | + const description = baseDescription |
| 93 | + .split(' ') |
| 94 | + .slice(Math.ceil(Math.random() * 10)) |
| 95 | + .join(' '); |
| 96 | + const price = 200 + Math.random() * 800; |
| 97 | + |
| 98 | + // Make short version of description |
| 99 | + const descriptionShort = description.slice(0, 200); |
| 100 | + const priceStr = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(price); |
| 101 | + |
| 102 | + const placeholders = { |
| 103 | + title, |
| 104 | + beds, |
| 105 | + description, |
| 106 | + descriptionShort, |
| 107 | + priceStr, |
| 108 | + }; |
| 109 | + |
| 110 | + return replacePlaceholders(template, placeholders); |
| 111 | +} |
| 112 | + |
| 113 | +function getRandomItem(list) { |
| 114 | + return list[Math.floor(Math.random() * list.length)]; |
| 115 | +} |
| 116 | + |
| 117 | +function replacePlaceholders(str, placeholders) { |
| 118 | + let replacedStr = str; |
| 119 | + Object.keys(placeholders).forEach(placeholder => { |
| 120 | + replacedStr = replacedStr.replaceAll(`{{${placeholder}}}`, placeholders[placeholder]); |
| 121 | + }); |
| 122 | + |
| 123 | + return replacedStr; |
| 124 | +} |
| 125 | + |
| 126 | +const template = `<figure class="result-image"> |
| 127 | + <img src="https://api.lorem.space/image/house?w=350&h=250" alt="{{title}}" data-image /> |
| 128 | +</figure> |
| 129 | +
|
| 130 | +<div class="result-content"> |
| 131 | + <div> |
| 132 | + <h3>{{title}}</h3> |
| 133 | +
|
| 134 | + <div class="tags"> |
| 135 | + <span class="tag">{{beds}}</span> |
| 136 | + </div> |
| 137 | + </div> |
| 138 | +
|
| 139 | + <div class="long-text"> |
| 140 | + <div class="long-text__short"> |
| 141 | + {{descriptionShort}}<button type="button" data-long-text-open>... Read more</button> |
| 142 | + </div> |
| 143 | +
|
| 144 | + <div class="long-text__long"> |
| 145 | + {{description}} |
| 146 | + <button type="button" data-long-text-close>Read less</button> |
| 147 | + </div> |
| 148 | + </div> |
| 149 | +
|
| 150 | + <div class="select"> |
| 151 | + <button type="button" data-select> |
| 152 | + <div aria-hidden="true" class="icon">+</div> |
| 153 | + <div>Select</div> |
| 154 | +
|
| 155 | + <div class="select__price"> |
| 156 | + <div class="price__amount">{{priceStr}}</div> |
| 157 | + <div class="price__quantity-label">/night</div> |
| 158 | + </div> |
| 159 | + </button> |
| 160 | + </div> |
| 161 | +
|
| 162 | + <div class="options"> |
| 163 | + <div class="field"> |
| 164 | + <select> |
| 165 | + <option value="0">0 rooms</option> |
| 166 | + <option value="1">1 room</option> |
| 167 | + </select> |
| 168 | + </div> |
| 169 | +
|
| 170 | + <div class="field"> |
| 171 | + <select> |
| 172 | + <option value="1">1 guest</option> |
| 173 | + <option value="2">2 guests</option> |
| 174 | + </select> |
| 175 | + </div> |
| 176 | + </div> |
| 177 | +</div>`; |
0 commit comments