You don't have to do anything. Why would you want to do anything at all then?
If we're talking conceptual difference, and use a richer example, one directly accesses a low-level construct (document.querySelectorAll
) to perform some effort in-place (cohesion). If we talk domain, then, the alternative is to refer to items in their higher-level (application) construct, indirecting and encapsulating some core logical concern into a detail of the system instead of it's purpose (adhesion).
We often solve for outcome first, then determine purpose, which is backwards (IMO) except in fast prototyping early on, when little forethought is possible or practical. Basing reference naming constructs on software issues ("I need to get the buttons") makes the argument harder to see, in my opinion, of the why we make references except "don't do something (whatever) twice in the same place".
For instance, if our current view is products of some sort (for some reason) located in a DOM element list of products, we could abstract it into reference handles that represent the core domain constructs, making the how a concern of some other (lower, further in) part of the code.
If you think about it, the fact of the buttons existing is less interesting than what they accomplish (and why we would want to refer to them in the first place). High-level references then deal with the core problem domain, abstracting and indirecting the software domain issue of locating and performing some action against those things.
Using rich reference naming constructs helps to simplify your codebase's outcomes while making the purpose and intention of those logical constructs more compelling and easier to infer.
const productNotFound = id => {
throw 'Product '+id+' not found.'
}
const products = ids => ids === undefined ? [...productList] : ids.map(product)
const product = id => {
const find = (found, item) => {
return found || (item.dataset && item.dataset.id == id && item) || null
}
return [...productList].reduce(find, null) || productNotFound(id)
}
const queryProducts = () => document.querySelectorAll('#products > li')
const resetProduct = id => {
let item = product(id)
item.classList.remove(...item.classList)
return item
}
const addProduct = id => {
let item = document.createElement('li')
item.dataset.id = id
item.textContent = 'Product '+id
document.getElementById('products').appendChild(item)
}
const addProducts = ids => {
ids.map(addProduct)
productList = queryProducts() // Update core reference after addProduct
}
const selectProduct = id => resetProduct(id).classList.add('selected')
const archiveProduct = id => resetProduct(id).classList.add('archived')
const removeProduct = id => resetProduct(id).classList.add('deleted')
let productList = queryProducts()
try {
addProducts([1, 2, 3, 4, 5])
console.log(products().map(item => item.textContent))
console.log(products([1, 2]).map(item => item.textContent))
selectProduct(2)
selectProduct(3)
removeProduct(3)
archiveProduct(1)
removeProduct(4)
addProducts([10, 11, 12])
selectProduct(12)
setTimeout(() => [1,3,5].map(archiveProduct), 3000)
removeProduct(500)
} catch (error) {
console.log('Error!', error)
}
.selected {
font-weight: bold;
color: blue;
}
.archived {
font-weight: bold;
color: #aaa;
}
.archived::after {
content: ' (Archived)'
}
.deleted {
text-decoration: line-through;
color: red;
}
<ul id="products"></ul>