Home > Back-end >  Removing the first element and appending a new one to replace it with jQuery
Removing the first element and appending a new one to replace it with jQuery

Time:05-04

Can someone explain why the following fiddle is failing.

I'm simply removing the first square element and appending a new square element on button click.

I'm going to start pulling my hair out soon. It seems like it works as expected on the first button click, but then weird things start to happen on consecutive clicks.

$('.click-me').on('click', function() {
  let _this = $(this),
      _main = $('.main');

  // Mark the first square for removal and update the classes of the other squares to replace it.
  _main.children().each(function() {
    let _square = $(this);

    if ( _square.hasClass('square1') )
      _square.removeClass('square1').addClass('remove');
    if ( _square.hasClass('square2') )
      _square.removeClass('square2').addClass('square1');
    if ( _square.hasClass('square3') )
      _square.removeClass('square3').addClass('square2');
    if ( _square.hasClass('square4') )
      _square.removeClass('square4').addClass('sqaure3');
    if ( _square.hasClass('square5') )
      _square.removeClass('square5').addClass('sqaure4');
  });
  
  // Remove square1 and append a new square5.
  _main.find('.remove').remove();
  _main.append('<span ></span>');
});
.main {
  position: relative;
  display: flex;
  margin-top: 20px;
}
.main span {
  width: 35px;
  height: 35px;
  background-color: #2e852e;
  border: 1px solid #000;
}
.main .square1,
.main .square5 {
  background-color: #a5a551;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button >Click Me!</button>
<div >
  <span ></span>
  <span ></span>
  <span ></span>
  <span ></span>
  <span ></span>
</div>

CodePudding user response:

  • It'll be much easier to use data attribute to save the number .. like data-number="1"and the selector will be like [data-number="1"]
  • Then no need for square1,square2... even no need for add/removeClass at all
  • The arrange of code make difference .. remove the first square before you decrease the number by one

This is how it should looks like .. Click the button once until you get the action and to understand whats doing on

$(document).on('click', '.click-me:not(.running)', function() {
  let _this = $(this),
      _main = $('.main');
  // add Class running
  _this.addClass("running");
  // Remove [data-number="1"] and append a new square with [data-number="5"].
  _main.find('.square[data-number="1"]').remove();
  console.log("First square removed")
  // decrease number by 1
  setTimeout(function(){
     $(".square").each(function(){
      let number = $(this).attr("data-number");
      $(this).attr("data-number" ,  number - 1);
    });
    console.log("Decrease number by one so the second square will be first and background will change");
  } , 2000);
  // append the new square
  setTimeout(function(){
  _main.append('<span  data-number="5"></span>');
  console.log("The new sqaure added");
  // remove Class running
  _this.removeClass("running");
  } , 4000);
});

$(document).on('click', '.click-me.running', function() {
  console.log("Action running");
});
.main {
  position: relative;
  display: flex;
  margin-top: 20px;
  transition : 0.4s;
}
.main span {
  width: 35px;
  height: 35px;
  background-color: #2e852e;
  border: 1px solid #000;
}
.square[data-number="1"],
.square[data-number="5"] {
  background-color: #a5a551;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button >Click Me!</button>
<div >
  <span  data-number="1"></span>
  <span  data-number="2"></span>
  <span  data-number="3"></span>
  <span  data-number="4"></span>
  <span  data-number="5"></span>
</div>

I added setTimeout to give the code a little bit of time to show the action.. because without the setTimeout the code will run but Unnoticeable

Additional: I added class running to the button to prevent multiple clicks while the action is running

CodePudding user response:

I believe the objective is to remove the first square and move the remaining squares into the position that precedes it and add a new square to the end. In the example provided each square has a different background color assigned by class which never changes -- this way it is easy to see that the actual squares are moving into their correct positions. The other classes ".b1" to ".b5" will be reassigned to each square on every click of the button. As the first one is removed, it's replacement is immediately placed at the end. The new square will be assigned ".b5" and a color class determined by an external counter and an array. Also, each square has the CSS pseudo-element ::before { content: attr(class) } to show classes as text content.

The trick is to count every click (that's done with count) and every significant iteration (that's done with the first parameter of .each(i). Set some arrays as references (positions array for classes ".b1" to ".b5" and rainbow array for color classes).

const rainbow = ['green', 'blue', 'red', 'gold', 'violet'];
const positions = ['b1', 'b2', 'b3', 'b4', 'b5'];
let count = 0;

$('button').on('click', function() {
  $('b').each(function(i, e) {
    if (i === 0) {
      e.remove();
      $('main').append(`<b class='${rainbow[count]} b5'></b>`);
    } else {
      let current = positions[i];
      let previous = positions[i - 1];
      $(e).addClass(previous);
      $(e).removeClass(current);
    }
  });
  count  ;
  if (count > 4) count = 0;
});
main {
  display: flex;
  margin-top: 20px;
}

b {
  display: inline-block;
  width: 35px;
  height: 35px;
  padding: 10px;
  border: 1px solid #000;
  text-align: center;
}

b::before {
  content: attr(class);
}

.green {
  background: green;
  color: white
}

.blue {
  background: blue;
  color: gold;
}

.red {
  background: red;
  color: white;
}

.gold {
  background: gold;
  color: blue;
}

.violet {
  background: violet;
  color: black;
}
<button>Click Me!</button>
<main>
  <b ></b>
  <b ></b>
  <b ></b>
  <b ></b>
  <b ></b>
</main>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

CodePudding user response:

Well folks thanks to everyone who answered but the simple answer was a 2 letter change, literally:

if ( _square.hasClass('square4') )
      _square.removeClass('square4').addClass('sqaure3');
if ( _square.hasClass('square5') )
      _square.removeClass('square5').addClass('sqaure4');

The genius in me has put 'sqaure3' and 'sqaure4' in the if statements above. Simply fixing those to the expected 'square3' and 'square4', respectively, solves the problem.

PS1: I'd like to mention that my jsFiddle is definitely not formatted with max efficiency in mind (as has been proven by the other answers on here). It was just a quick sample I put together to try and demonstrate a layout problem I was having (nothing to do with this question) but ended up with a different problem due to my spelling mistake.

PS2: I personally think this question is a waste of time to go through for anyone else but Stackoverflow is being silly and won't let me delete the question. Sorry about that.

  • Related