Reputation: 1851
I have a song submission form on my site. When an artist uploads a song, I developed JS to calculate the beats per minute (BPM) and duration of the song. Once calculated, it populates the two corresponding input boxes (Duration and BPM) on the form. The code I have works perfectly if a single song is submitted, however, the form allows multiple song uploads per form submission. Since these new input boxes are added dynamically, I'm not quite sure how I can get the new songs to dynamically update the value of the bpm/duration since the new input boxes are loaded by JS. There's a decent amount of code below, please let me know if you need more clarification:
The user can dynamically add another song by clicking the "Add another song" button loaded through JS:
<div id="dynamicInput"></div>
<input type="button" id="add-song-btn" value="Add another song" onClick="addInput('dynamicInput');">
Corresponding JS:
var counter = 1;
var limit = 20;
function addInput(divName)
{
if(counter == limit)
{
alert("You have reached the limit of adding " + counter + " inputs");
}
else
{
var newdiv = document.createElement('div');
{
newdiv.innerHTML = "<hr>Song #" + (counter + 1) + " " + "<input type='hidden' role='uploadcare-uploader' data-max-size='120000000' name='song-submission'data-crop='disabled' /><input type='text' id='duration' name='submit-duration' placeholder='Duration: '3:11'><input type='number' id='bpm' name='submit-bpm' placeholder='BPM: 128'>";
document.getElementById(divName).appendChild(newdiv);
}
counter++;
setTimeout(function () {
uploadcare.initialize();
}, 0);
}
};
When a user uploads a song by using the "Choose a file" button, this is the corresponding input box:
<input name="song-submission" type="hidden" role="uploadcare-uploader" data-max-size="120000000" data-file-types="mp3" data-crop="disabled">
When uploaded, the submission creates a src url that I add to an audio HTML src tag that triggers two different JS functions. When the upload completes the JS code widget.onUploadComplete
runs. Here's that audio tag and the corresponding code that loads the src url into that html code:
<audio id="myAudio" controls onloadedmetadata="setDuration()">
<source src="">
</audio>
JS
var widget = uploadcare.Widget('[role=uploadcare-uploader]');
widget.onUploadComplete(function(info) {
var previewUrl = info.cdnUrl;
document.getElementById('myAudio').src = info.cdnUrl;
//code to calculate BPM is found here///
document.getElementById('bpm').value = Math.round(top[0].tempo);
};
};
request.send();
});
When the src url is added to the HTML audio id="myAudio"
tag, the JS waits until onloadedmetadata
to run a function ( setDuration()
) to determine the duration:
function setDuration() {
var time = document.getElementById('myAudio').duration;
// code to calculate duration //
ret += "" + mins + ":" + (secs < 10 ? "0" : "");
ret += "" + Math.round(secs);
document.getElementById('duration').value = ret;
return ret;
}
So here is where I'm at, and my code does not work with dynamically loaded input boxes. First thing is that once a new song submission input is added, there would be two input boxes with role=uploadcare-uploader
which would cause issues with the widget
JS variable right off the bat, I think that's where we need to start first. Any thoughts?
Upvotes: 1
Views: 1648
Reputation: 1844
When you initialize new uplaodcare instance (deferred) it doesn't register onUploadComplete
events for those newly created widgets, because only on that moment when you execute uploadcare.Widget('[role=uploadcare-uploader]');
first child only (another problem of your code) gets the event listener registered.
Element argument can be a DOM element, a jQuery object, or a CSS selector. >If element’s argument is a CSS selector, only the instance for the first >matched element will be returned. To make sure you get the right instance >when there are multiple widgets on the page, select by id or pass in a DOM element.
Execute your code after each uploadcare instance creating and use unique selector to get proper widget:
setTimeout(function () {
uploadcare.initialize();
var widget = uploadcare.Widget('#song-uploader-<1..N>'); // Generate this when DIV adding
widget.onUploadComplete(function(info) {
var previewUrl = info.cdnUrl;
document.getElementById('myAudio').src = info.cdnUrl;
//code to calculate BPM is found here///
document.getElementById('bpm').value = Math.round(top[0].tempo);
};
};
request.send();
});
}, 0);
You can omit the handler into its own function to keep code cleaner and write widget.onUploadComplete(calculateBPM)
Upvotes: 2
Reputation: 431
Have a look at the API documentation:
There it says:
Initializes widget instances once (either single-upload, or multi-upload) on every element with role="uploadcare-uploader" attribute in a container, specified by selector or DOM element. If container is absent the document is used. Returns array of new initialized widgets, or empty array if no new instances were initialized.
var widgets = uploadcare.initialize('#my-form');
widgets; // [widget1, widget2, multipleWidget1, ...]
var widgets = uploadcare.initialize();
So from that point you can go on. (e.g. giving the input box an id and call your js function on every input box with that id).
Upvotes: 0