Matthew Egan
Matthew Egan

Reputation: 43

AppendTo but each new append is unique but still uses the same jquery

I'm wondering if it's possible to on each appendTo make the new div unique but still use the same jquery.

As you can see in the mark-up below, each new div shares the same jquery so doesn't work independently.

Within my Javascript i'm selecting the ID to fire each function.

I've tried just adding + 1 etc to the end of each ID, but with that it changes the name of the ID making the new created DIV not function.

I've thought of using DataAttribues, but i'd still have the same issue having to create multiple functions all doing the same job.

Any ideas?

Thanks

$(function() {
  var test = $('#p_test');
  var i = $('#p_test .upl_drop').length + 1;

  $('#addtest').on('click', function() {
    $('<div class="file-input"><div class="input-file-container upl_drop"><label for="p_test" class="input-file-trigger">Select a file...<input type="file" id="p_test" name="p_test_' + i + '" value=""class="input-file"></label></div><span class="remtest">Remove</span><p class="file-return"></p></div>').appendTo(test);
    i++;
  });


  $('body').on('click', '.remtest', function(e) {
    if (i > 2) {
      $(this).closest('.file-input').remove();
      i--;
    }
  });
});


var input = document.getElementById( 'file-upload' );
var infoArea = document.getElementById( 'file-upload-filename' );

input.addEventListener( 'change', showFileName );

function showFileName( event ) {

  // the change event gives us the input it occurred in
  var input = event.srcElement;

  // the input has an array of files in the `files` property, each one has a name that you can use. We're just using the name here.
  var fileName = input.files[0].name;

  // use fileName however fits your app best, i.e. add it into a div
  textContent = 'File name: ' + fileName;

  $("#input-file-trigger").text(function () {
      return $(this).text().replace("Select a file...", textContent);
  });
}
/*
#### Drag & Drop Box ####
*/

.p_test{
  display: inline-block;
}

.upl_drop{
  border: 2px dashed #000;
  margin: 0px 0px 15px 0px;
}

.btn--add p{
  cursor: pointer;
}

.input-file-container {
  position: relative;
  width: auto;
}
.input-file-trigger {
  display: block;
  padding: 14px 45px;
  background: #ffffff;
  color: #1899cd;
  font-size: 1em;
  cursor: pointer;
}
.input-file {
  position: absolute;
  top: 0; left: 0;
  width: 225px;
  opacity: 0;
  padding: 14px 0;
  cursor: pointer;
}
 .input-file:hover + .input-file-trigger,
 .input-file:focus + .input-file-trigger,
 .input-file-trigger:hover,
 .input-file-trigger:focus {
  background: #1899cd;
  color: #ffffff;

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
  <div class="file-input">
    <div class="input-file-container upl_drop">
      <input class="input-file" id="file-upload" type="file">
      <label tabindex="0" for="file-upload" id="input-file-trigger" class="input-file-trigger">Select a file...</label>
    </div>
    <div id="file-upload-filename"></div>
  </div>
  <button class="btn--add" id="addtest">
          Add
  </button>
</div>

Upvotes: 1

Views: 127

Answers (1)

Rory McCrossan
Rory McCrossan

Reputation: 337560

I'd advise against using incremental id attributes. They become a pain to maintain and also make the logic much more complicated than it needs to be.

The better alternative is to use common classes along with DOM traversal to relate the elements to each other, based on the one which raised any given event.

In your case, you can use closest() to get the parent .file-input container, then find() any element within that by its class. Something like this:

$(function() {
  var $test = $('#p_test');

  $('#addtest').on('click', function() {
    var $lastGroup = $test.find('.file-input:last');
    var $clone = $lastGroup.clone();
    $clone.find('.input-file-trigger').text('Select a file...');
    $clone.insertAfter($lastGroup);
  });

  $test.on('click', '.remtest', function(e) {
    if ($('.file-input').length > 1) 
      $(this).closest('.file-input').remove();
  }).on('change', '.input-file', function(e) {
    if (!this.files)
      return;
      
    var $container = $(this).closest('.file-input');
    $container.find(".input-file-trigger").text('File name: ' + this.files[0].name);
  });
});
.p_test {
  display: inline-block;
}

.upl_drop {
  border: 2px dashed #000;
  margin: 0px 0px 15px 0px;
}

.btn--add p {
  cursor: pointer;
}

.input-file-container {
  position: relative;
  width: auto;
}

.input-file-trigger {
  display: block;
  padding: 14px 45px;
  background: #ffffff;
  color: #1899cd;
  font-size: 1em;
  cursor: pointer;
}

.input-file {
  position: absolute;
  top: 0;
  left: 0;
  width: 225px;
  opacity: 0;
  padding: 14px 0;
  cursor: pointer;
}

.input-file:hover+.input-file-trigger,
.input-file:focus+.input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
  background: #1899cd;
  color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
  <div class="file-input">
    <div class="input-file-container upl_drop">
      <input class="input-file" type="file">
      <label tabindex="0" for="file-upload" class="input-file-trigger">Select a file...</label>
    </div>
    <div class="file-upload-filename"></div>
  </div>
  <button class="btn--add" id="addtest">Add</button>
</div>

Note that I've made a couple of other optimisations to the code. Firstly it now makes a clone() of the last available .file-input container when the Add button is clicked. This is preferred over writing the HTML in the JS file as it keeps the two completely separate. For example, if you need to update the UI, you don't need to worry about updating the JS now, as long as the classes remain the same.

Also note that you were originally mixing plain JS and jQuery event handlers. It's best to use one or the other. As you've already included jQuery in the page, I used that as it makes the code easier to write and more succinct.

Finally, note that you didn't need to provide a function to text() as you're completely over-writing the existing value. Just providing the new string is fine.

Upvotes: 1

Related Questions