Generate dynamic borders

Asked

Viewed 55 times

1

I have the following form:

<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<button>GERAR</button>

On it the user selects the value of the border width in the following block:

<div class="forma"></div>

I created the method gerar()

const gerar = _ => {
  let widths = [];
  cps.forEach(c => widths.push(`${c.value}px`));
  frm.style.borderStyle = 'solid';
  frm.style.borderWidth = widths.join(' ');
};

which traverses the object cps and adds its value to the object widths, then sets the property borderWidth with the values of the object widths.

So far so good, as can be seen below the code works normally.

let btn = document.querySelector('button');
let cps = document.querySelectorAll('[type="number"]');
let frm = document.querySelector('.forma');

const gerar = () => {
  let widths = [];
  cps.forEach(c => widths.push(`${c.value}px`));
  frm.style.borderStyle = 'solid';
  frm.style.borderWidth = widths.join(' ');
};

btn.onclick = gerar;
input[type="number"] {
  width: 40px;
}
.forma {
  height: 100px;
  margin: 14px;
  width: 100px;
}
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<button>GERAR</button>

<div class="forma"></div>

The problem

One of these fields may have its value higher than the others, ex: 0 10 60 10 / 50 20 20 40.

I have to define the property borderColor and the field that has its highest value, will have the different color, ex:

0 10 60 10  = "transparent #f7f7f7 #069 #f7f7f7"
50 20 20 40 = "#069 #f7f7f7 #f7f7f7 #f7f7f7"

How can I solve ?

2 answers

1

I believe you should store only the entry in one {Array} to be able to filter and find the highest value index... imagining that its entry follows the same order scheme defined by the attribute border-color (top,right,bottom,left) you can use reduce():

let fakeEntires = [13, 8, 13, 2]

function getIndexFromMajor(itens) {
    return itens.reduce((iMax, x, i, arr) => x > arr[iMax] ? i : iMax, 0)
}

let index = getIndexFromMajor(fakeEntires)

console.log(`index:${index}, value:${fakeEntires[index]}`)

However this method will return only the first occurrence (index) if there are duplicate values, I believe that in this case the ideal would not return in this case, you can use the operator "Spread" ... and new Set() to verify duplicate entries if there are any, switch() to check the number of resulting entries and go through the original matrix in search of the higher value index.

You did not specify how you would apply this "different color" to the border so I will presume to use red in the following example where the "colors" are also in a matrix respecting the order defined by the attribute border-color.

If the search is not satisfactory the negative index -1 will not apply color matrix changes, complete example:

let btn = document.querySelector('button');
let cps = document.querySelectorAll('[type="number"]');
let frm = document.querySelector('.forma');

const gerar = () => {

  function getIndexFromMajor(itens) {
    // check duplicates values
    let filter = [...new Set(itens)]
    // expected four (4) itens
    if ( filter.length === 4 ) {
        // no duplicates
        return itens.reduce((iMax, x, i, arr) => Number(x) > Number(arr[iMax]) ? i : iMax, 0)
    } else {
        switch(filter.length){
            case 3:
            case 2:
                // check number of clear duplicates
                let target = Math.max(...filter),
                    result = []
                itens.forEach((item, index) => {
                    if ( item === target ) {
                        result.push(index)
                    }
                })
                return (result.length === 1) ? result[0] : -1
            break
            case 1:
            default:
                -1
            break
        }
    }
  }

  let inputs = [],  // user input values
      borders = [], // borders width
      defColors = [ // default colors
         '#27bc3c', // top
         '#27bc3c', // right
         '#27bc3c', // bottom
         '#27bc3c'  // left
      ]
      
  cps.forEach(c => inputs.push(Number(c.value))); // number
  inputs.forEach(i => borders.push(`${i}px`));    // string
  
  frm.style.borderStyle = 'solid';
  frm.style.borderWidth = borders.join(' ');
  // change border color (on great border)
  defColors[getIndexFromMajor(inputs)] = '#ff0000' // red
  frm.style.borderColor = defColors.join(' ')
};

btn.onclick = gerar;
input[type="number"] {
  width: 40px;
}
.forma {
  height: 100px;
  margin: 14px;
  width: 100px;
}
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<button>GERAR</button>

<div class="forma"></div>

1


You can do as follows: No click starts by generating the same color for all Borders, whereas the ones that are 0px end up not being visible. Then finds the largest using Math.max on the values of <input> and also thinks which <input> has the largest, and based on its position assigns the borderColor what matters, this being one of the four available:

  • borderTopColor
  • borderRightColor
  • borderBottomColor
  • borderLeftColor

Example:

let btn = document.querySelector('button');
let cps = document.querySelectorAll('[type="number"]');
let frm = document.querySelector('.forma');

const borders = ["borderTopColor", "borderRightColor", 
                 "borderBottomColor", "borderLeftColor"];

const gerar = () => {
  let widths = [];
  cps.forEach(c => widths.push(`${c.value}px`));
  frm.style.borderStyle = 'solid';
  frm.style.borderWidth = widths.join(' ');
  frm.style.borderColor = '#b7b7b7'; //aplica a cor normal a todos
  
  //pega em cada um dos inputs e mapeia para um array apenas com os numeros
  let nums = [...cps].map(caixa => Number(caixa.value)); 
  
  let maior = Math.max(...nums); //acha o maior     
  let posicaoMaior = nums.findIndex(x => x === maior); //acha a posição do maior
  frm.style[borders[posicaoMaior]] = '#069'; //mete a cor diferente no maior
};

btn.onclick = gerar;
input[type="number"] {
  width: 40px;
}
.forma {
  height: 100px;
  margin: 14px;
  width: 100px;
}
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<button>GERAR</button>

<div class="forma"></div>

Note: I put a gray slightly darker just to see better.

I found the biggest using the Operator spread and its position using findIndex.

The above example works for when you only have one element larger than the others. If there is the possibility of having more than one, and you want everyone to have the color greater just change a little the way it is applied, using for example a forEach and comparing each value if it is equal to the greater:

let btn = document.querySelector('button');
let cps = document.querySelectorAll('[type="number"]');
let frm = document.querySelector('.forma');

const borders = ["borderTopColor", "borderRightColor", 
                 "borderBottomColor", "borderLeftColor"];

const gerar = () => {
  let widths = [];
  cps.forEach(c => widths.push(`${c.value}px`));
  frm.style.borderStyle = 'solid';
  frm.style.borderWidth = widths.join(' ');
  frm.style.borderColor = '#b7b7b7'; 
  
  let nums = [...cps].map(caixa => Number(caixa.value)); 
  let maior = Math.max(...nums);

  //aplica o maior agora com um forEach
  cps.forEach((caixa, posicao) => { 
    if (Number(caixa.value) === maior){
      frm.style[borders[posicao]] = '#069';
    }
  });
};

btn.onclick = gerar;
input[type="number"] {
  width: 40px;
}
.forma {
  height: 100px;
  margin: 14px;
  width: 100px;
}
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<input type="number" min="0" value="0" />
<button>GERAR</button>

<div class="forma"></div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.