How can I count appended elements and calculate their style? - javascript

I'm trying to create a local static page with an image uploader and a preview gallery to which append images while the user selects them. To be more specific, the image uploader is an <input type="file" multiple="multiple"> and whenever a user selects an image I have a <div class="preview__gallery"></div> to which I append the selected images from the File Explorer.
Here comes the problem. After the <img> tags are being attached to the gallery div I'd like to count them using jQuery .length property so I can then apply some custom style using .css() based on the counting results.
Seems like I can not target those images by class. I've read all across the internet and even here on SO for similar anwers and in fact I've already tried a lot of options. I've tried to use jQuery deferred functions with $.when(firstFunction()).done(otherFunction()), I've tried to use MutationObserver to control changes that occurs on the gallery div and I've also tried event delegation but without success because I don't have any event that triggers the function. It just have to start after the images are appended.
I'm currently calling the counting function as a callback after the function that appends the images. I put a console.log('before') on the first function and a console.log('after') on the counting one, and it seems that the callback is working correctly. However, the .length property is not working and this is because jQuery is not being able to target the image element on the DOM. In fact, using a console.log($('.preview__thumb')) outputs an empty jQuery object.
Here's the HTML:
<div class="concorso__body">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="single__upload">
<div class="upload__field">
<input type="file" accept="image/*" multiple="multiple" class="drag__area" id="file__input">
<div class="upload__text">
<i class="bi bi-plus"></i>
<span>Trascina o clicca per caricare immagini</span>
</div>
</div>
<div class="preview__gallery" id="the__gallery"></div>
</div>
</div>
<div class="col-lg-4"></div>
</div>
</div>
</div>
And here's the JS (using jQuery):
$(document).ready(function() {
$(function() {
function imagesPreview(input, placeToInsertImagePreview, callback) {
if (input.files) {
var filesAmount = input.files.length;
for (i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = function(event) {
$($.parseHTML('<img class="preview__thumbs">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
};
reader.readAsDataURL(input.files[i]);
console.log('before');
}
if(typeof callback === 'function') {
callback();
}
}
}
});
});

$(document).ready(function() {
$(function() {
function imagesPreview(input, placeToInsertImagePreview, callback) {
if (input.files) {
var filesAmount = input.files.length;
var count = 0; // Initialize a counter for the number of images
for (i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = function(event) {
$($.parseHTML('<img class="preview__thumb">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
count++; // Increment the counter after each image is added
if (count == filesAmount) { // Check if all images have been added
if (typeof callback === 'function') {
callback();
}
}
};
reader.readAsDataURL(input.files[i]);
}
}
}
// Call the imagesPreview function on file input change event
$('#file__input').on('change', function() {
var gallery = $('.preview__gallery');
imagesPreview(this, gallery, function() {
// This function is called after all images have been appended
var numImages = $('.preview__thumb').length;
console.log(numImages); // This should output the correct number of images
if (numImages > 5) {
gallery.css('background-color', 'red'); // Apply custom style if more than 5 images
}
});
});
});
});

Related

JS Code to verify if images have not loaded on a webpage [duplicate]

The goal: check if all images in the page are loaded. If yes, call to a function, if not, again to check if all images are loaded.
My try:
let checkImages = new Promise(resolve => { resolve(areCompleted()); });
function areCompleted() {
let images = document.querySelectorAll('img');
images = Array.from(images);
for (let i = 0; i < images.length; i++) {
if (!images[i].complete) {
return false;
}
}
return true;
}
If all images are completed, it resolves the promise with true, if not, false.
checkImages.then(completed => {
if (completed) {
completedFunction();
} else {
// Check again
}
});
If the response is true, call a function, if not... I don't know how to do the same check again, but I want to do that checking until the response is true.
This function will check for already loaded images and attach an event listener to all the others so that it can tell when every image in a given container is loaded...
function onImagesLoaded(container, event) {
var images = container.getElementsByTagName("img");
var loaded = images.length;
for (var i = 0; i < images.length; i++) {
if (images[i].complete) {
loaded--;
}
else {
images[i].addEventListener("load", function() {
loaded--;
if (loaded == 0) {
event();
}
});
}
if (loaded == 0) {
event();
}
}
}
var container = document.getElementById("container");
onImagesLoaded(container, function() {
alert("All the images have loaded");
});
<div id="container">
<img src="https://cdn.vox-cdn.com/thumbor/C1SdoDActSv8tPONx_OjwEobUjw=/0x0:1004x753/1200x800/filters:focal(0x0:1004x753)/cdn.vox-cdn.com/uploads/chorus_image/image/49523369/20150428-cloud-computing.0.jpg" />
<img src="https://images.techhive.com/images/article/2016/08/clouds-100678070-primary.idge.jpg" />
<img src="https://www.quali.com/wp-content/uploads/2016/04/101-HEADER-IMAGE.jpg" />
<img src="https://cdn.computerworlduk.com/cmsdata/features/3641280/cloud_istock_malerapaso_thumb800.jpg" />
</div>
This will still work if all the images have already loaded, due to being cached, or if there are no images in the container.
If you want to check all images in a page, simply change the container selector to the body instead...
var container = document.getElementsByTagName("body")[0];

Hide image preview on input click/change

I have created a image preview in JavaScript with some script i found here, I'm new to JS and now I am trying to replace the content instead of appending it. Here is the code
I've tried to put empty(), replaceWith(), remove(), just before appendTo() like other similar questions on SO, I've tried to create a function to hide the div, and nothing seems to work. I was wondering if anyone would know why these functions doesn't work, Am i doing something wrong?
//my html/php
echo '<input required type="file" name="filesToUpload[]" multiple
id="image_upload" accept=".jpg, .jpeg">';
echo '<div id="gallery-preview" class="gallery-preview"></div>';
//javascript
$(function() {
var imagesPreview = function(input, placeToInsertImagePreview) {
if (input.files) {
var filesAmount = input.files.length;
for (i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = function(event) {
$($.parseHTML('<img style="width:150px; height:75px; margin-left:2px; margin-right:2px; border:1px solid black; padding:2px;">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
}
reader.readAsDataURL(input.files[i]);
}
}
};
$('#image_upload').on('change', function() {
imagesPreview(this, 'div.gallery-preview');
});
});
When i create a function to hide() the div gallery-preview, the preview doesn't work anymore. Same for other functions mentioned above
I think you are trying to set it empty each time you run the function.
Modify the change function in your code to the following
$('#image_upload').on('change', function() {
//this line will set the contents to empty before you run your function
$('div.gallery-preview').html('');
//now call your function
imagesPreview(this, 'div.gallery-preview');
});

View and Hide Image Using Jquery

I am displaying images before upload using jquery, when i upload some new files i want to remove or hide the previous upload files here's my jquery code:
$(function()
{
// Multiple images preview in browser
var imagesPreview = function(input, placeToInsertImagePreview)
{
if (input.files)
{
var filesAmount = input.files.length;
for (i = 0; i < filesAmount; i++)
{
var reader = new FileReader();
reader.onload = function(event)
{
$($.parseHTML('<img class="p-3" width="350px" height="250px">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
}
reader.readAsDataURL(input.files[i]);
}
}
}
$('#file_input').on('change', function()
{
imagesPreview(this, 'div#viewUploadItems');
});
});
And my HTML Code:
<input type="file" name="images[]" id="file_input" class="deletable" multiple />
<div id="viewUploadItems"></div>
I try this code but this won't display any image.
$("#file_input").on("click",function()
{
$('input.deletable').val('');
$('#viewUploadItems').remove();
});
Perhaps you could take the following approach, where in your imagePreview() function you:
first call empty() on the preview selector to clear any prior image contents
then proceed to read and display any selected images, by using the FileReader API as you currently are (see below for revised approach)
Also, consider checking the type of the file object, to ensure that it is an image before attempting to display it via the following:
if (file.type.match("image.*")) {
/* file is image type, so attempt to preview it */
}
Bringing these ideas together, you could revise your code as follows:
$(function() {
function imagesPreview(input, targetSelector) {
/* Empty the target area where previews are shown */
$(targetSelector).empty();
/* Iterate each file via forEach in own closure */
Array.from(input.files).forEach(function(file) {
/* If file is image type proceed to preview */
if (file.type.match("image.*")) {
/* Create filereader and set it up for reading */
var reader = new FileReader();
reader.onload = function(event) {
/* Append a new image element, prepopulated with
required attrbutes, and assigned with p-3 class */
$(targetSelector).append($('<img>', {
width: '350px',
height: '250px',
src : reader.result
}).addClass('p-3'))
}
reader.readAsDataURL(file);
}
})
}
$('#file_input').on('change', function() {
imagesPreview(this, 'div#viewUploadItems');
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<input type="file" name="images[]" id="file_input" class="deletable" multiple />
<div id="viewUploadItems"></div>
Easier to clear the div before display : $(placeToInsertImagePreview).html("");
$(function()
{
// Multiple images preview in browser
var imagesPreview = function(input, placeToInsertImagePreview)
{
if (input.files)
{
$(placeToInsertImagePreview).html("");
var filesAmount = input.files.length;
for (i = 0; i < filesAmount; i++)
{
var reader = new FileReader();
reader.onload = function(event)
{
$($.parseHTML('<img class="p-3" width="350px" height="250px">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
}
reader.readAsDataURL(input.files[i]);
}
}
}
$('#file_input').on('change', function()
{
imagesPreview(this, 'div#viewUploadItems');
});
});

Rubberband with iScroll and jQuery mobile

The headline says it all. At start of my app I retrieve data from a php file (some divs) and append them to an wrapper-div. Around this wrapper-div (not called wrapper) is the iScroll wrapper.
iScroll is working, but there is a rubberband effect.
Here's the (index) HTML:
<div data-role="header" data-theme="c" data-position="fixed">
<h1>Title</h1>
</div><!-- /header -->
<div id="wrapper">
<div id="scroller">
<div data-role="content" id="content">
<div id="headlinesindex">
<div class="span3" id="9999999999"></div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
onBodyLoad();
});
</script>
And here's the javascript-file:
function onBodyLoad()
{
$.ajax({
url: "headlines_getter.php?last="+ $(".span3:last").attr('id') ,
success: function(html) {
if(html){
$("#headlinesindex").append(html);
setTimeout(function () {
myScroll.refresh();
}, 0);
}
}
});
}
function onDeviceReady()
{
var myScroll = new iScroll('wrapper');
}
I've played arround with the setTimeout as it is explained at iscroll.com, but it changes nothing... Hope you know what's wrong.
Thanks in advance. Best regards, John.
I had the same issue.
It came from the outer "wrapper" not being sized correctly in iscroll.
If it is sized the same size as the inner "scroller" height then the iscroll will have no where to go and rubber band.
I fixed it for me, and created a fork for others with the same issue:
https://github.com/meckdahl/iscroll
================================== Advanced Usage
Here is some addon functions I use to maintain my 20+ scroll containers in our Spine.JS mobile app:
For each page I set a specific wrapper like such:
<div id="wrapper2">
Then I dynamically create iScroll only if that page is loaded:
After the content for the page is loaded I call like such:
window.resetScroll(2)
window.setScrolling(true)
This will re-initialize iScroll for this page.
Here are the functions I define on my root page:
<script type="text/javascript">
// maximum wrapper index = 23 currently (9/12/12)
var myScrolls = [];
myScrolls.length = 29; // Scrolls to look for wrapper1-30
var refreshScrolling = function() {
//console.log('refreshScrolling Active Scroll Items: ');
myScrolls.forEach( function(scrollItem){
scrollItem.refresh();
});
};
var refreshScroll = function(wrapperNumber) {
//console.log('refreshScroll wrapperNumber: wrapper' + wrapperNumber.toString());
var i = wrapperNumber;
setTimeout(function () {
(myScrolls[i-1]).refresh();
}, 100);
};
// This looks for and initializes and dynamic scrolls that Spine recently put in memory
// and have not been initialized yet.
var setScrolling = function() {
for (var i=1; i < myScrolls.length+1; i++){
if (($("#wrapper"+(i).toString()).length !== 0) ){
if((myScrolls[i-1] !== null) && (myScrolls[i-1] !== undefined)){
// Already setup
}
else{
myScrolls[i-1] = new iScroll('wrapper'+ (i).toString(),
{ hScroll: false, hScrollbar: false, vScrollbar: false });
created.");
}
}
}
}
// This must be called on a view with dynamic content to re-create the view to fit the potentially
// changing content size. It will only rebuild the one scroll whose index is passed in.
// The index should be the wrapper# for the view attached to the controller.
// Call setScrolling after this to catch any uninitialized views.
var resetScroll = function(wrapperNumber) {
var i = wrapperNumber;
// if (!(i in myScrolls)) continue; // skip nonexistent elements && !(myScrolls[i-1] )
if (($("#wrapper"+(i).toString()).length !== 0) ){
if( (myScrolls[i-1] !== null) && (myScrolls[i-1] !== undefined)){
// Destroy Skipped right now
myScrolls[i-1].destroy();
myScrolls[i-1] = null;
}
myScrolls[i-1] = new iScroll('wrapper'+ (i).toString(),
{ hScroll: false, hScrollbar: false, vScrollbar: false });
created.");
}
}
function loaded() {
setTimeout(function () {
setScrolling();
}, 100);
}
window.addEventListener('load', loaded, false);
</script>
I had the same problem with my custom script so I changed the code and now it's working nicely:
var myScroll;
function loaded() {
setTimeout(function(){
myScroll = new iScroll('wrapper');
myScroll.refresh();
} , 100 );
}
And I call it on "onDeviceReady":
function onDeviceReady()
{
loaded();
}
http://jsfiddle.net/Eccgy/
Check this may be help you
Here is a simple iscroller that would help .
its very easy to implement
include scripts and jsut add an attribute data-iscroll to the div, which you need the effect.
https://github.com/watusi/jquery-mobile-iscrollview

Can a JQuery plugin be called on a JQuery chain of methods or does it need to be called straight after a selector?

I'm having difficulty getting my textarea to expand vertically automatically.
I have some code to help make textarea auto expand vertically and for some reason it works when I clean out all my JS and provide a selector with reference to textarea e.g. $('textarea').autoGrow();
Calling the plugin on a chain of methods stops it from working. E.G.
micropostBox.hide().removeClass("micropost_content")
.addClass("micropost_content_expanded").show().autoGrow();
I established the plugin code works so copied all my working code to the same page and applied the autoGrow code to my textarea but it seems to be unresponsive. I noticed that the plugin I'm using the code from uses bind and unbind methods. In my code I use on and off methods from JQuery and wondering if this could be why the auto resizing of my textarea is not working?
Here is the code:
http://jsfiddle.net/erU5J/101/
autogrow plugin js code
$(function($) {
$.fn.autoGrow = function() {
return this.each(function() {
var txtArea = $(this);
var colsDefault = txtArea.attr('cols');
var rowsDefault = txtArea.attr('rows');
var updateSize = function() {
var linesCount = 0;
var lines = txtArea.attr('value').split('\n');
for (var i = lines.length - 1; i >= 0; --i) {
linesCount += Math.floor((lines[i].length / colsDefault) + 1);
}
if (linesCount >= rowsDefault) {
txtArea.attr('rows', linesCount + 1);
}
else {
txtArea.attr('rows', rowsDefault);
}
};
txtArea.unbind('.autoGrow').bind('keyup.autoGrow', updateSize).bind('keydown.autoGrow', updateSize).bind('change.autoGrow', updateSize);
});
};
});
my js code
$(function() {
$("div.microposts").on("focus", "textarea#micropostBox", function() {
var micropostForm = $(this).parent(),
micropostBox = micropostForm.find('textarea#micropostBox'),
micropostButton = micropostForm.find("input#micropostButton"),
xButton = micropostForm.find("div.xButton");
micropostBox.prop('rows', 7);
micropostForm.find('div#micropostOptions').removeClass('micropostExtraOptions');
micropostForm.find('div#postOptions').show();
$.trim(micropostBox.val()) == '' ? micropostButton.addClass("disabledMicropostButton").show()
:
micropostButton.prop('disabled', false);
micropostBox.hide().removeClass("micropost_content").addClass("micropost_content_expanded").show().autoGrow();
xButton.show();
micropostButton.prop('disabled', true);
micropostBox.off().on("keypress input change", function() {
micropostButton.prop({
disabled: !$.trim($(this).val()) != ''
});
$.trim($(this).val()) != '' ? micropostButton.removeClass("disabledMicropostButton").addClass("activeMicropostButton")
:
micropostButton.removeClass("activeMicropostButton").addClass("disabledMicropostButton");
});
xButton.on('click', function() {
micropostBox.removeClass("micropost_content_expanded").addClass("micropost_content");
micropostForm.find('div#micropostOptions').addClass('micropostExtraOptions');
micropostBox.val("");
micropostForm.find('div#postOptions').hide();
xButton.hide();
micropostButton.hide();
micropostBox.removeAttr('style');
micropostBox.prop('rows', 0);
micropostForm.find('.imagePreview > img').remove();
micropostForm.find('.imagePreview').hide();
});
});
});
$(function() {
$('div.microposts').on('click', 'li#addImage', function() {
var form = $(this).parents('form#new_micropost'),
fileField = form.find('input#micropost_image');
fileField.trigger('click');
});
});
$(function() {
$('input#micropost_image').change(function(evt) { //.off() make sautoresize work
var image = evt.target.files[0],
form = $(this).parents('form#new_micropost'),
imagePreviewBox = form.find('div.imagePreview'),
reader = new FileReader();
reader.onload = function(evt) {
var resultdata = evt.target.result,
img = new Image();
img.src = evt.target.result;
imagePreviewBox.show().prepend(img);
};
reader.readAsDataURL(image);
});
});​
textarea
<textarea class="micropost_content" cols="40" id="micropostBox" name="micropost[content]" placeholder="" rows="0"></textarea>
It would be best to view a working example on jsfiddle. My aim is to have the auto resizing of textarea working before and after an image is added to the page using the image upload button in the textarea.
Kind regards
It depends if the method preceding the plugin call returned the jQuery object containing the elements to where the plugins need to be attached.
Here are a few examples of methods that do and do not return the elements you started with:
$('element') //get an element
.contents() //get an elements contents
.wrapAll('<div>') //wrapAll contents with div and returns the contents, not wrapper
.parent() //the wrapper
.parent() //the element
.myPlugin() //we attach a plugin to element
$('<div>')
.appendTo('body') //appendTo returns the same element, the div
.myPlugin() //attaches to the div
$('element') //get an element
.text() //get its text
.myPlugin() //chaining isn't possible since text() returns a string
Better read the docs for every method in jQuery and what it returns. Some DOM methods usually return the same element, some don't, and some don't return elements but values.
In summary, can plugins be attached after chains? YES, and it depends.
Refer to the jQuery's documentation
http://docs.jquery.com/Plugins/Authoring#Maintaining_Chainability

Categories

Resources