Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slider FileReader JS Multiple Image Upload (Incrementing Index)

I am trying to make a JavaScript multiple image uploader that uploads image previews to a slider, but I am having some issues. So far it looks like I was able to get the images to upload into the slider, but the problem seems to happen with my i variable - when I try to increment it, it stays the same, not allowing my next and previous slider arrows from working. If anyone knows how to get this slider working properly, I would appreciate the help.

JS Code:

$('#_uploadImages').click(function() {
    $('#_imagesInput').click()
})

$('#_imagesInput').on('change', function() {
    handleFileSelect();
});

function handleFileSelect() {
    //Check File API support
    if (window.File && window.FileList && window.FileReader) {

        var files = event.target.files; //FileList object
        var output = document.getElementById("frames");

        for (var i = 0; i < files.length; i++) {
            var file = files[i];

            //Only pics
            if (!file.type.match('image')) continue;

            var picReader = new FileReader();
            picReader.addEventListener("load", function (event) {
                var picFile = event.target;

                console.log(event);

                current_i = i;
                prev_i = current_i - 1;
                next_i = current_i + 1;

                //var div = document.createElement("div");
                //div.innerHTML = div.innerHTML + "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
                //output.insertBefore(div, null);

                ////output.innerHTML = output.innerHTML + "<img class='thumbnail' style='max-width:500px' src='" + picFile.result + "'" + "title=''/>";  // TODO: Enter Title
                output.innerHTML = output.innerHTML + '<li id="slide-' + current_i + '" class="slide">' + "<img src='" + picFile.result + "'" + "title=''/>" + '<nav>' + '<a class="prev" href="#slide-' + prev_i + '">&larr;</a>' + '<a class="next" href="#slide-' + next_i + '">&rarr;</a>' + '</nav>' + '<li>';  // TODO: Enter Title
            });
            //Read the image
            picReader.readAsDataURL(file);
        }
        //output.innerHTML = output.innerHTML + '<li class="quicknav">' + '<ul>' + '<li><a href="#slide-1"></a></li>' + '<li><a href="#slide-2"></a></li>' + '<li><a href="#slide-3"></a></li>' + '</ul>' + '</li>'
    } else {
        console.log("Your browser does not support File API");
    }
}

JSFiddle: http://jsfiddle.net/Hybridx24/yfr57u6w/

like image 313
Hybrid Avatar asked Nov 03 '15 06:11

Hybrid


2 Answers

The problem with the code is that by the time the load event executes - the for loop has already incremented. So if two images are added - the value of i when the load event is executing is already 2.

One way to solve this is to add the value of i to an array and retrieve it in the event listener one by one:

var arrFilesCount = [];

for (var i = 0; i < files.length; i++) {
     arrFilesCount.push(i);   //push to array

     var file = files[i];

     //Only pics
     if (!file.type.match('image')) continue;

        var picReader = new FileReader();
        picReader.addEventListener("load", function (event) {
        var picFile = event.target;

        current_i = arrFilesCount.shift(); // get from array instead of using i
        prev_i = current_i - 1;
        next_i = current_i + 1;
        ...
        ...

Corresponding jsFiddle here


Now, this array can also be used for determining the first/last element and thereby using this to go from last to first element. Because we cannot be sure when the event listener will execute (say if there are 100 images the first event listener may execute when the count of loop has reached 5 or 10), so I've used two loops instead of one. The first loop just to populate the array.

var arrFilesCount = [];
for (var i = 0; i < files.length; i++) {
     arrFilesCount.push(i);
}

Lets use this to find the first and last elements

current_i = arrFilesCount.shift();
if(current_i === 0){
    prev_i = files.length - 1;   //This is for the first element. The previous slide will be the last image. (i=length-1)
}
else{
    prev_i = current_i - 1;
}
if(arrFilesCount.length === 0){
    next_i = 0;     //This is for the last element. The next slide will be the first image (i=0)
}
else{
    next_i = current_i + 1;
}

See this jsFiddle.


Finally, there can be scenarios where the user first adds a couple of images then clicks on upload button again and adds a couple of more images. In this case we need to correct the existing href. The elements which we need to correct are the next of last and prev of first. This can be done using:

var start = $(output).find('li').length;
var end = start+ files.length;

if(start !== 0){
    $(output).find('li > nav > a.prev').first().attr('href','#slide-' + (end-1));
    $(output).find('li > nav > a.next').last().attr('href','#slide-'+start);
}

So the final jsFiddle will be something like this.

like image 181
Taleeb Avatar answered Oct 23 '22 12:10

Taleeb


Substituted .append() for .innerHTML ; created variable idx to increment .slide li elements ids ; added delegated click event to nav a elements ; added .bind() with this set to picReader , i passed as parameter to picReader onload event ; added file.name to title attribute of img element ; added "dot" navigation with thumbanail of images beneath #frames ; title to arrow navigation

var idx = -1, re = /(.*)(?=\.)/;

    $('#_uploadImages').click(function() {
      $('#_imagesInput').click();
    });

    $('#_imagesInput').on('change', function(event) {
      handleFileSelect(event);
    });

    $(document).on("click", ".slider .slide nav a, .nav a", function(e) {
      e.preventDefault();
        $(".slide").hide()
        .filter(":has(img[title^="+e.target.title.match(re)[0]+"])").show();
    });

    function handleFileSelect(event) {
      //Check File API support
      if (window.File && window.FileList && window.FileReader) {

        var files = event.target.files; //FileList object
        var output = document.getElementById("frames");

        for (var i = 0; i < files.length; i++) {
          var file = files[i];

          var picReader = new FileReader();
          picReader.onload = function(index, event) {
            ++idx;
            var picFile = event.target;
            var slides = $(".slider li[id^=slide]");
            // TODO: Enter Title
            $(output)
              .append('<li id="slide-' 
                + idx 
                + '" class="slide">' 
                + "<img src='" 
                + picFile.result
                // set `title`
                + "'title="
                //`index` : `i`
                + files[index].name 
                + "/>" 
                + '<nav>' 
                + '<a class="prev">&larr;</a>' 
                + '<a class="next">&rarr;</a>' 
                + '</nav>' 
                + '</li>');
            // add title to `nav a` elements
            if (file.name === files[files.length - 1].name) {
              $(".nav").empty();
              $("nav a").each(function(i, el) {
                if ($(el).closest("[id^=slide]").prev("[id^=slide]").length 
                    && $(el).is("nav a:nth-of-type(1)")) {
                      $(el).attr("title", 
                        $(el).closest("[id^=slide]")
                        .prev("[id^=slide]").find("img").attr("title")
                      )
                }

                if ($(el).closest("[id^=slide]").next("[id^=slide]").length 
                    && $(el).is("nav a:nth-of-type(2)")) {
                      $(el).attr("title", 
                        $(el).closest("[id^=slide]")
                        .next("[id^=slide]").find("img").attr("title")
                      )
                }

                if ($(el).is(".slider [id^=slide]:first a:first")) {
                  $(el).attr("title", 
                    $("[id^=slide]:last").find("img").attr("title")
                  )
                }

                if ($(el).is(".slider [id^=slide]:last a:last")) {
                  $(el).attr("title", 
                    $("[id^=slide]:first").find("img").attr("title")
                  )
                };
              });
              
              $(".slider img").each(function(i, el) {
                 $(".nav").append(
                   $("nav a[title^="
                     +$(el).attr("title").match(re)[0]
                     +"]:first")
                     .clone().html(el.outerHTML)
                 )
              })
            }
          }.bind(picReader, i);

          //Read the image
          picReader.readAsDataURL(file);
        };

      } else {
        console.log("Your browser does not support File API");
      }
    }
* {
  margin: 0;
  padding: 0;
  /*transition*/
  -webkit-transition: all 1s ease;
  -moz-transition: all 1s ease;
  -o-transition: all 1s ease;
  transition: all 1s ease;
}
body {
  padding: 30px;
}
/* Slider */

.slider {
  height: 250px;
  left: 50%;
  margin: -125px -225px;
  position: absolute;
  top: 48%;
  width: 450px;
  /*box-shadow*/
  -webkit-box-shadow: 0 0 5px #000;
  -moz-box-shadow: 0 0 5px #000;
  box-shadow: 0 0 5px #000;
}
.slider .frames {
  height: 250px;
  position: relative;
  list-style-type: none;
}
.slider .frames .slide {
  height: 250px;
  list-style: none;
  position: absolute;
  width: 450px;
}
.slider .slide:target {
  z-index: 100
}
.slider .frames .slide img {
  height: 250px;
  width: 450px;
}
.slider .frames .slide nav a {
  background: hsla(0, 0%, 0%, .75);
  color: #fff;
  font-size: 16px;
  line-height: 50px;
  margin-top: -25px;
  opacity: 0;
  position: absolute;
  text-align: center;
  text-decoration: none;
  top: 50%;
  width: 50px;
  visibility: hidden;
  z-index: 10;
}
.slider:hover .frames .slide nav a {
  opacity: 1;
  visibility: visible;
}
.slider .slide nav a:hover {
  cursor: pointer;
}
.slider .frames .slide nav .prev {
  /*border-radius*/
  -webkit-border-radius: 0 25px 25px 0;
  -moz-border-radius: 0 25px 25px 0;
  border-radius: 0 25px 25px 0;
  left: 0;
}
.slider .frames .slide nav .next {
  /*border-radius*/
  -webkit-border-radius: 25px 0 0 25px;
  -moz-border-radius: 25px 0 0 25px;
  border-radius: 25px 0 0 25px;
  right: 0;
}
.slider .frames .slide nav a:hover {
  background: #000
}
.slider .quicknav {
  bottom: 0;
  font-size: 0;
  opacity: 0;
  position: absolute;
  text-align: center;
  width: 100%;
  z-index: 100;
}
.slider:hover .quicknav {
  opacity: .9
}
.slider .quicknav li {
  display: inline-block
}
.slider .quicknav a {
  background: hsla(0, 0%, 100%, .9);
  border: 1px solid hsla(0, 0%, 0%, .9);
  /*border-radius*/
  -webkit-border-radius: 100%;
  -moz-border-radius: 100%;
  border-radius: 100%;
  display: block;
  height: 10px;
  margin: 10px 5px;
  text-decoration: none;
  width: 10px;
}
.slider .quicknav a:hover {
  background: hsla(0, 0%, 50%, .9)
}

.nav {
 width:100%;
 text-align:center;
}

.nav a {
  display:inline-block;
  background:transparent;
  border-radius:50%;
  border:4px solid transparent;
  width:24px;
  height:24px;
  margin:4px;
}

.nav a img {
  width:22px;
  height:22px;
  border-radius:50%;
}


.slider #one:target ~ .quicknav a[href="#one"],
.slider #two:target ~ .quicknav a[href="#two"],
.slider #three:target ~ .quicknav a[href="#three"],
.slider #four:target ~ .quicknav a[href="#four"],
.slider #five:target ~ .quicknav a[href="#five"] {
  background: hsla(0, 0%, 0%, .9);
  border-color: hsla(0, 0%, 100%, .9);
  background: rgb(244, 246, 245);
  /*linear-gradient*/
  background: -webkit-gradient(linear, left top, left bottom, color-stop(rgba(244, 246, 245, 1), 0.01), color-stop(rgba(203, 219, 219, 1), 1), color-stop(rgba(216, 216, 216, 1), 1));
  background: -webkit-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -moz-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -o-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -webkit-gradient(linear, left top, left bottom, color-stop(1%, rgba(244, 246, 245, 1)), color-stop(100%, rgba(203, 219, 219, 1)), color-stop(100%, rgba(216, 216, 216, 1)));
  background: -webkit-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -moz-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -o-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#f4f6f5', endColorstr='#d8d8d8', GradientType=0);
  /*box-shadow*/
  -webkit-box-shadow: inset 0 0 3px #000, 0 0 2px rgba(0, 0, 0, .5), 0 2px 3px #666;
  -moz-box-shadow: inset 0 0 3px #000, 0 0 2px rgba(0, 0, 0, .5), 0 2px 3px #666;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button id="_uploadImages" class="btn btn-primary">Upload Images</button>

<form id="_imagesForm" action="" method="post">
  <input id="_imagesInput" accept="image/*" type="file" style="display:none" multiple>
</form>

<div id="_displayImages">
  <div class="slider">
    <ul id="frames" class="frames">

    </ul>
    <div class="nav"></div>
  </div>
</div>

jsfiddle http://jsfiddle.net/yfr57u6w/24/

like image 35
guest271314 Avatar answered Oct 23 '22 10:10

guest271314