It's a web project. The code is working. The question is a design pattern question to write code that's more elegant, you know.
I've created a Codepen of this question.
I created a css grid WITHOUT using 'grid' but using float and clear.
I know such project works better written in React but it's just a try.
The grid I'm creating is entirely recreated when calling the render(world) method.
I'm not using any HTML outside of javascript (typescript). I'm only using 4 CSS classes:
.block-first, .block-lasts {
width: 20px;
height: 20px;
border-radius: 20px;
float: left;
}
.block-first {
clear: both;
}
.image-in-block {
width: 20px;
height: 20px;
}
.container {
}
In the grid are square blocks of equal size being 1- a uniformly colored block or 2- a block containing an image.
So I first fill up a matrix 20x20 of instances of classes that implements a IBlock interface containing a getProperty() method. Those classes are 1- ColorBlock or 2- ImageBlock. Below are these classes and the interface:
interface IBlock {
getProperty() : string;
}
class ImageBlock implements IBlock{
url: string;
constructor(url: string) {
this.url = url;
}
getProperty() : string {
return this.url;
}
}
class ColorBlock implements IBlock{
color: string;
constructor(hexColor: string) {
this.color = hexColor;
}
getProperty() : string {
return this.color;
}
}
My problem is that I need to use different code to render each. For the ColorBlock I'm going with a colored div with background: '#ff00ff'; But for the ImageBlock I'm going with a nested img tag instead of a background-image: url('https://...');.
For distinguishing between the different classes, I'm using object_instance.constructor.name, I think that's the whole problem.
Now the rest of my code:
Matrix prepping:
const world = new Array(20);
for(let i=0; i<20; i++) {
world[i] = new Array(20);
world[i].fill(new ColorBlock('#ff00ff'));
}
The function rendering each block as ColorBlock or ImageBlock:
function html_process_image_or_color_block(dom, block_instance) {
const html_block = dom;
const block = block_instance;
const block_property = block.getProperty();
switch (block.constructor.name) {
case 'ImageBlock':
const image_tag = document.createElement('img');
image_tag.src = block_property;
image_tag.classList.add('image-in-block');
html_block.appendChild(image_tag);
break;
case 'ColorBlock':
html_block.setAttribute('style', `background: ${block_property};`)
break;
default: break;
}
// return html_block;
}
And finally the render(world) function that uses the 20x20 Matrix and the above function:
function render(world) {
const html_container = document.createElement('div');
html_container.classList.add('container');
for(let i=0; i<20; i++) {
//code for rendering first left most block
const html_first_block = document.createElement('div');
html_first_block.classList.add('block-first');
const first_block = world[i][0];
html_process_image_or_color_block(html_first_block, first_block);
html_container.appendChild(html_first_block);
//code for rendering all other blocks on its right
for(let j=1; j<20; j++) {
const html_block = document.createElement('div');
html_block.classList.add('block-lasts');
const block = world[i][j];
html_process_image_or_color_block(html_block, block);
html_container.appendChild(html_block);
}
}
document.body.innerHTML = '';
document.body.appendChild(html_container);
}
render(world);
In my opinion the code is terrible :) and I appreciate all your time and effort!
function html_process_image_or_color_block-- if the parameters are objects, not primitive values, thenhtml_block&blockdeclarations seem unnecessary.dom&block_instanceparameters point to the original/external Objects. Then explicitly returninghtml_blockis likewise unnecessary. \$\endgroup\$