Dynamically add input values to dynamically added input boxes - javascript

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?

PROBLEM
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.
SOLUTION
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)

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).

Related

Creating images with Ajax

I'm using a Bootstrap theme and I wanted the image gallery on the theme's image display page to load via AJAX.
Photos come as JSON with AJAX but I couldn't get them to show on the page.
The gallery related part of this theme from the original JS file:
var productGallery = function () {
var gallery = document.querySelectorAll('.product-gallery');
if (gallery.length) {
var _loop8 = function _loop8(i) {
var thumbnails = gallery[i].querySelectorAll('.product-gallery-thumblist-item'),
previews = gallery[i].querySelectorAll('.product-gallery-preview-item');
for (var n = 0; n < thumbnails.length; n++) {
thumbnails[n].addEventListener('click', changePreview);
} // Changer preview function
function changePreview(e) {
e.preventDefault();
for (var _i3 = 0; _i3 < thumbnails.length; _i3++) {
previews[_i3].classList.remove('active');
thumbnails[_i3].classList.remove('active');
}
this.classList.add('active');
gallery[i].querySelector(this.getAttribute('href')).classList.add('active');
}
};
for (var i = 0; i < gallery.length; i++) {
_loop8(i);
}
}
}();
Data from JSON file with Ajax:
some AJAX code..
if (slidePhotos.photos) {
for (let x= 0; x< slidePhotos.photos.length; x++) {
document.getElementById('gallery_photos_div').innerHTML += '<div class="product-gallery-preview-item" id="' + x+ '"><img src="' + slidePhotos.photos[x].url + '" alt=""></div>';
document.getElementById('gallery_thumbs_div').innerHTML += '<a class="product-gallery-thumblist-item" href="#' + x+ '"><img src="' + slidePhotos.photos[x].url + '"></a>';
}
}
The HTML Code is generated but unfortunately the images do not change when I click on it.
Sample JSON:
[
{
"url":"https://example.com/image1.jpg",
"url":"https://example.com/image2.jpg"
}
]
Can you tell me where I made a mistake?
I see a couple of problems here:
It looks like the code that you're using to populate your slideshow with new images from that JSON file appends a div.product-gallery-preview-item to your slideshow container, but doesn't append a corresponding .product-gallery-thumblist-item. In your productGallery function, your click handler is being bound to the latter, not the former. You'll want to make sure those target thumbnail elements are added as well as the preview ones.
Presumably, your productGallery function is fired when the page/DOM is first loaded to initialize the slideshow. The click event handlers that control the functionality of your slideshow are bound only to the elements that are present when the function runs. If you're not running this function repeatedly when appending content via AJAX (I hope you're not, as this would bind duplicate event handlers to the elements already in the slideshow), you'll need to ensure that your new elements are primed to respond to click in the same way that your existing ones are. You have a couple of options here:
Refactor productGallery so that it can be invoked repeatedly with the same slideshow, e.g: adding a :not(.slideshow-processed) to the the end of your .product-gallery-thumblist-item and .product-gallery-preview-item query selectors, and then adding the slideshow-processed class to these elements after binding your event handlers so they won't be processed again during subsequent invocations of productGallery.
Refactor productGallery to use event delegation (where a parent element listens for an event that occurs on one of its child elements). This would allow you to bind your event handler to the .product-gallery container just once, and have it fire for any preview/thumbnail pair that gets appended to the slideshow, without having to re-invoke productGallery. You can read more about event delegation at https://javascript.info/event-delegation.
Hopefully this points you in the right direction. Happy coding!

Prevent createElement from creating multiple identical images

I am going to try and be as thorough as possible on this so stick with me.
My program takes user input entered on a form. This input goes through a range of if statements that will set variables to true, these variables that are set to true will then use createElement to create img inside predetermined divs. There will always be at least one img created, and up to 4 imgs at the most. This is all completed by a button click.
I learned createElement will continuously createElement with each button click posting the img right next to the original img and will do this infinitely or until the page is reloaded.
My solution was to create a counter that incremented each time the button was clicked. That took care of any extra createElements. Then I thought if the user first entered a large number that will create 2 separate and individual images say... image 1 and image 2 (which is desired), then if the user enters a lower number that only creates image 1, then image 2 would still be posted (unwanted). Of course a page refresh would clear the images created and avoid this.
So I'm not quite sure what to do...
Ideally when the button is pressed a second time then all old createElements would be cleared, and all new createElements would populate.
I removed the counter for the purpose of this question
poster__#'s are the called id's...
const movieTime = 30;
var userTimeAvailable = 25;
const button = document.getElementById("calculate");
button.onclick = function calculateTime() {
if (userTimeAvailable < movieTime) {
console.log(
"You have enough time to watch " +
Math.round((userTimeAvailable / movieTime) * 100) +
"% of the movie."
);
displayMoviePoster1 = true;
}
moviePosterGenerator();
}
function moviePosterGenerator() {
if (displayMoviePoster1 === true) {
var par = document.getElementById("poster__2");
var theatricalMoviePoster = document.createElement("img");
theatricalMoviePoster.src = "imgURL";
par.appendChild(theatricalMoviePoster);
}
}
<button class="button" id="calculate">calculate</button>
<div class="movie-poster__container" id="posters">
<div class="movie-poster" id="poster__1"></div>
<div class="movie-poster" id="poster__2"></div>
<div class="movie-poster" id="poster__3"></div>
<div class="movie-poster" id="poster__4"></div>
</div>

Deleted images still being shown

For some reason, my website still showing images that were already deleted from the specified folder and I have no idea why that's happening and how to solve that.
Process: When the button to delete all admins is pressed, it calls a PHP function that truncate the tables administration, adminimg and login, delete all images from a folder related to id's on table administration with unlink(), and create a registry on administration table with id=1(auto_increment) and name="abc".
Problem: I have a jQuery function that display a specific admin information on textboxes, verify the value in the textbox for the adminID, and display the image associated to that id. After executing the process above, when i call the jQuery function, it display correctly the id=1 and name="abc" but shows the deleted image associated to the admin with id=1 before truncate the tables.
jQuery function (if necessary)
$(".btneditadmin").click( e =>{
let textvalues = displayDataAdmin(e);
let id = $("input[name*='idadmin']");
let name = $("input[name*='nameadmin']");
id.val(textvalues[0]);
nome.val(textvalues[1]);
var img_url = 'Images/Administration/admin'+$("#idadmin").val()+'.jpg';
$("#admin-image").attr('src',img_url);
});
function displayDataAdmin(e) {
let id = 0;
const td = $("#tbody tr td");
let textvalues = [];
for (const value of td){
if(value.dataset.id == e.target.dataset.id){
textvalues[id++] = value.textContent;
}
}
return textvalues;
}
If you're sure that image isn't there anymore then it's caching issue and something like this would take care of it
let img_url = 'Images/Administration/admin'+$("#idadmin").val()+'.jpg';
img_url += '?' + new Date().getTime() ; // cache killer
$("#admin-image").attr('src', img_url);
However, you're calling that function no matter what so I would suggest a onload/error check
​$('#admin-image').load(function(){ // when loaded successfully
console.log('success');
}).error(function(){ // when theres an error
$(this).remove()
// or you could replace it with a default image
$(this).attr('src', '/images/default.jpg');
});​​​​​

Submit text value into iframe input

I want to send a text value from the parent page to the iframe < input >. It's all on the same domain and folder.
Was trying this code:
function addNick(nick) {
var txt = parent.document.getElementById('input').value;
parent.document.getElementById('mess').value = 'to[' + nick + '] ' + txt;
parent.document.getElementById('mess').focus();
}
<iframe src="input.php" name="input" id="input" scrolling="auto"></iframe>
Text is triggered in mess.php to be submitted with link to the above iframe input.
name
which is printed in index.php with js
$(document).ready(function(){
setInterval(function(){//setInterval() method execute on every interval until called clearInterval()
$('#load_posts').load("mess.php").fadeIn("slow");
//load() method fetch data from mess.php page
}, 1000);
});
So basically the addNick code won't work.
Any help will be much appreciated.
FINAL SOLUTION
function addNick(nick) {
var iframe = document.getElementById('iframe');
//the element you want to target:
var mess = iframe.contentWindow.document.getElementById("mess").value += `${nick}, `;
}
So first get the iframe element by id, then target the element within the iframe..
so if input.php has an <h1></h1> tag this will set the innerHTML to what the parameter nick holds..
function addNick(nick) {
var iframe = document.getElementById('iframe');
//the element you want to target:
var mess = iframe.contentWindow.document.getElementsByTagName("H1")[0];
mess.innerHTML = `to ${nick}`
}
On how to access dom of iframe, check out this example:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_element_iframe

On page reload, check boxes get selected and then disappear automatically within a second

I have a problem with a function that i'm writing.
Basically, I'm simulating a landing page. The landing page will contain a querystring. What I'm doing is to deserialize it on a button click and show(to check) the checkboxes defined in the querystring in the HTML.
Below is the click function which contains two functions.
The first one "simulateLandingPage" performs the url change.
The second one called "selectFacetsAutomatically" deserializes the "landingUrl" path and checks automatically the checkboxes in the HTML page according to what is defined in the querystring.
The problem that I'm encountering is that when I click on the "reload-page" button everything works but the checkboxes get selected just for a second and then disappear automatically quickly without any reason. Hence the page won't show the selected checkboxes in the end but just this weird thing.
Can anyone help? I'm pretty new to this and i'm stuck.
Thanks a lot!
$(".reload-page").click(function() {
var landingUrl = "size:4,10,16|base_colour:1,4|brand:53,3392,12767";
simulateLandingPage(landingUrl);
selectFacetsAutomatically(landingUrl);
return false;
});
function simulateLandingPage(landingUrl){
window.location.href = "refinements.html?refine="+ encodeURIComponent(landingUrl);
return false;
}
function selectFacetsAutomatically(landingUrl){
var facetGroup = [];
var selectedFacets = [];
var facetType;
//split string when it finds the pipe symbol
$.each(decodeURIComponent(landingUrl).split(/\|/), function (i, val) {
selectedFacets.push(val);
console.log("val", selectedFacets[i].split(/\:/)[1].split(/\,/));
facetType = selectedFacets[i].split(/\:/)[0];
facetGroup = selectedFacets[i].split(/\:/)[1].split(/\,/);
$.each(facetGroup, function(i,val){
var facetToBeSelected = facetType + "_" + val;
$('[data-id='+facetType+']').find("#"+facetToBeSelected).prop('checked', true);
});
});
return false;
}

Categories

Resources