I'm trying to make two upload buttons. One is for an account logo.
.col-sm-6
.wrapper
Account Logo
#account_logo
= image_tag #account.logo.url, :class => 'account_logo'
= file_field_tag 'account[logo]', :accept => 'image/png,image/jpeg'
%a.btn.btn-default{:href => '#'} Choose file
%span.notice
Only PNG or JPG please.
The other is for an email logo.
.col-sm-6
.wrapper
Email Logo
#email_logo
= image_tag #account.email_logo.url, :class => 'email_logo'
= file_field_tag 'account[email_logo]', :accept => 'image/png,image/jpeg'
%a.btn.btn-default{:href => '#'} Choose file
%span.notice
Only PNG or JPG please.
I've added javascript so it works for both. However, there is something very wrong going on. I've added alerts to try and debug and they tell me that it's all going into #email_logo even when I click on the #account_logo button. Can some javascript wizard please help me?
:javascript
var logo = ["#account_logo", "#email_logo"];
for (var i = 0; i < logo.length; i++) {
var on_change = (logo[i] + ' input[type=file]');
var on_click = (logo[i] + ' a.btn');
var logo_img = (logo[i] + ' img');
$(document).on('click', on_click , function() {
alert("it is now " + on_click);
$(on_change).click();
alert("it is now " + on_change);
});
$(document).on('change', on_change, function() {
alert("it is now " + on_change);
var filelist = $(this).get(0).files;
var reader = new FileReader();
reader.onload = function (e) {
var url = e.target.result;
alert("it is now " + logo_img);
$(logo_img).attr('src', url);
}
reader.readAsDataURL(filelist[0]);
});
}
It might be because you are using a for(var i = ...) {} loop construct instead of logo.forEach(function(element) { ... }).
The reason this might be happening is because JavaScript does not have block scoping but function scoping, which means your loop variable i is available in the entire function, and your .on('change', ...) handlers will refer to this i as a variable, not as whatever value that variable had when you registered the handler.
Try using a logo.forEach(function(element) { ... } construct, and instead of setting var on_change = (logo[i] + 'input[type=file]');, do var on_change = (element + 'input[type=file]');.
Edit:
Ah ha, #Frost found the problem - variable scope. I would have used an iterator myself, and missed here the effect scope would have on event binding.
Given you're already using jQuery, and are probably after much better browser compatibility than forEach provides, I would highly recommend using the jQuery.each function. You may as well also inline the logos array, and pass the click event directly too, giving:
$(["#account_logo", "#email_logo"]).each(function(logo) {
$(logo + " input[type=file]").on("click", $(logo + " a.btn").click);
// etc.
});
Original response:
This seems like a lot of code for not much problem description. Are you saying that the event handlers are both applied to #email_logo, or that both buttons do the action desired of #email_logo? I haven't done file upload before, but from a quick review, the logic looks fine.
It would be easier to see the issue if you used console.log rather than alert, and pasted the results here, along with a description of how the log was produced.
I would prefer to comment this, but I don't have enough reputation.
Related
I need to pass a json object to function, which is as mentioned below in a href, but this JS code is not getting evaluated. So can anyone suggest a workaroud or a solution for this?
function function_test(option,jsonObj){
displayMessage(str);
}
function function_prepare_div(){
var str ="";
var jsonResposneObj = getJson();//function to get jsonResponseObj
for(i=0;i<jsonResponseObj.length;i++){
str += "<a href='function_test( " + i + "," + jsonResposneObj.dataObj[i] +")'>1. " + jsonResposneObj.dataObj[i].objName + "</a></br>";
}
return str;
}
P.S. I cannot return the jsonResponse after function call.
Instead of using inlined JS, append the element using proper DOM methods, and then attach an event listener.
Eg something like
const a = container.appendChild(document.createElement('a'));
a.href = function_test(1, jsonResponse);
a.textContent = '1. ' + function_test(1, jsonResponse);
(make sure function_test returns a URL)
Functions (or any JavaScript for that matter) don't belong in an <a href=""> in the first place.
Hyperlinks are for navigation, not JavaScript hooks. Using them just to trigger some JavaScript is an incorrect use of the <a> tag. It was commonplace 20+ years ago (before we had web standards), but is woefully outdated and downright incorrect today. Using a hyperlink to trigger JavaScript is semantically incorrect and can cause issues for people who rely on assistive technologies (like screen readers) to navigate a page.
Just about any element that is valid in the body of a web page supports a click event and most are better suited to what you want to do.
What you need to do is register your function as the callback to the click event of some element, like this:
// An example of a JSON response
var jsonResponse = '{"key1":10,"key2":true,"key3":"foo"}';
// Get reference to any element that supports a click event that
// can be safely used as a JavaScript trigger
var span = document.getElementById("fakeLink");
// Set up an event handling callback function for the click event of the element
span.addEventListener("click", function(){
// Call the function and pass it any arguments needed
processJSON(1, jsonResponse, this);
});
// Do whatever you need to do in this function:
function processJSON(val, json, el){
console.log(val, json);
el.textContent = val + json;
}
/* Make element look & feel like a hyperlink */
#fakeLink { cursor:pointer; text-decoration:underline; }
#fakeLink:active { color:red; }
<span id="fakeLink">Click Me</span>
var json = {
foo: 'bar'
};
var func = function(data) {
alert(data.foo);
}
$('body').append("<button onclick='func(" + JSON.stringify(json) + ")'>BUTTON</button>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I have a problem with some javascript in Internet Explorer.
It works fine in other browsers.
I have the following method, that changes the src property of an images and when this happens a download of that image should start. See below:
for (var i = 0; i < imagesStartedDownloading.length; i++) {
if (imagesStartedDownloading[i] == false && responseItems[i] == true) {
console.log("image", i);
var url = baseurl + "/ImageDownload/?imageName=" + hash + "_" + imageDegrees[i] + ".jpg" + "&r=" + Math.random();
imagesStartedDownloading[i] = true;
images.eq(i).attr("src", url);
}
}
The problem is that in when changing this property Internet Explorer starts an endless loop of downloading images. Notice that i have put a console.log in the for-loop. I can confirm that this for-loop does not run in an endles loop. It is only run once for each image that should be downloaded. So that is not the problem.
The behaviour can actually be seen on this page: http://www.energy-frames.dk/Konfigurator. Hit F12 and check in the network tab. Make a change to image on the homepage so a download of new images is started, e.g. Bredde(Width in english), see below:
When this change is made new images are downloaded in an endless loop(it happens almost every time in IE). See below of what you could change
I have really spent a lot of time debugging in this and i cant see why it behaves like this in IE but not in all other browsers.
So does anyone have any idea why this happens? Or have some suggestions on what i could try?
EDIT:
#gxoptg
I have tried what you suggested. using "javascript:void 0" like this:
var newimg = $("<img class='rotator" + (isMainImage ? " active" : "") + "' src='javascript:void 0' />");
and this:
img.attr("src", "javascript:void 0");
gives me this error:
On the other hand, if i completely remove the line img.attr("src", "");
in the imgLoadError method, then i see that images are not downloaded in an endless loop. On the other hand they are not displayed. So am i using the javascript:void 0 wrong?
When i do the following:
img.attr("src", "void(0)");
Then the there is not endless loop but the image wont appear in IE - still works fine in chrome.
Here’s the reason:
for (var i = 0; i < totalnumberofimages; i++) {
var url = "";
var isMainImage = i == currentDragImg;
var newimg = $("<img class='rotator" + (isMainImage ? " active" : "") + "' src='' />");
newimg.on("error", imgLoadError);
newimg.on("load", imgLoaded);
imgcontainer.append(newimg);
}
Note the var newimg = $(...) line. In Internet Explorer, setting an empty src attribute on an image triggers the error event. Due to the error, the imgLoadError function is called. It looks like this:
function imgLoadError(e) {
var img = $(e.currentTarget);
var imgSrc = img.attr("src");
if (imgSrc.length > 0 && img.width() <= 100) {
setTimeout(function () {
var imgUrl = img.attr("src");
img.attr("src", "");
img.attr("src", imgUrl);
}, 200);
}
}
In this function, you run img.attr("src", ""), which sets the empty src attribute, triggers the error event, and calls imgLoadError function again. This causes the endless loop.
To prevent the error (and therefore the endless loop), set image source to "javascript:void 0" instead of "" in both code pieces. This source is valid and should work properly.
(According to comments, all the code is located in /Assets/Scripts/directives/image.rotation.directive.js file.)
An alternative solution is to set the src attribute to a valid, minimal, Base64 encoded image, as in http://jsfiddle.net/leonardobraga/1gefL8L5/
This would avoid triggering the endless error handling and it doesn't impact the code size that much.
I have a div
<div id='cards'>
Which I want to fill with images based on some logic. But only when images are first loaded into memory. Otherwise, through onerror I wanna fill in some text..
function pasteCard(card, to){
if (typeof(card) == 'string')
card = [card];
var image = [];
for (var i = 0; i < card.length; i++) {
image[i] = new Image();
image[i].src = '/sprites/open/' + card[i] + '.png';
image[i].onload = function() {
pasteImage(to, image[i]);
}
image[i].onerror = function() {
pasteText(to, card[i]);
}
// alert(card[i]) #1
}
function pasteImage(to, image) {
to.append(image);
}
function pasteText(to, text) {
// alert(card[i]) #2
to.append(text);
}
}
pasteCard(['ABC123', 'DEF456', 'GHI789'], $('#cards'));
But this isn't working.
Problem/weirdness: If only #2 alert is active it returns nothing. But strangely if #1 alert is also active it does kinda work... (but still doesn't load my images, and mostly fails too when other code is involved)
Question: Why is it not working without #1 alert (at least in that jsfiddle)
suggestions?: what should I do?
Onload and onerror events are fired (executed) outside the scope of your function so your variables will be undefined. In the event method you have access to this which is the image object. You can set a data attribute to each image and access that in your error event.
Here is an example:
http://jsfiddle.net/7CfEu/4/
The callbacks are not in the same scope as your image array is - therefor you need to declare a variable then will "connect the scopes" and use it inside the callbacks
also the i variable probably changes until the callback is fired - so by using it inside the callback you will get undefined behavior
for (var i = 0; i < card.length; i++) {
var current_card = card[i];
var current_image = new Image();
current_image.onload = function() {
pasteImage(to, current_image);
}
current_image.onerror = function() {
pasteText(to, current_card);
}
current_image.src = '/sprites/open/' + current_card + '.png';
image[i] = current_image;
}
Fiddle: http://jsfiddle.net/7CfEu/6/
(Also - closing the div tag is never a bad idea)
Just in case anyone ends up here for same reason I did.
Was going crazy because onload and onerror were not firing in the page I was building. Tried copy pasting
var myimage = new Image();
myimage.onload = function() { alert("Success"); };
myimage.onerror = function() { alert("Fail"); };
myimage.src = "mog.gif" //Doesn't exist.
Which was working within codepen and random otherwise blank pages.
Turns out the problem I was having was that I was doing AJAX requests earlier in the page. This involved authorization which in turn involved a call to
setRequestHeader();
This was resulting in a net::ERR_FILE_NOT_FOUND error instead of the expected GET mog.gif 404 (Not Found)
This seemed to prevent proper triggering of events.
Revert with
xhr.setRequestHeader("Authorization", "");
I'm new to programming and was wondering how to make a customized alert that shows the id or class name of the object when I click on it. My site has a picture of 8 different animals, and I want it so that every time I click on one of the animals there's an alert with "This is a (animal's name)". Why won't my javascript code below work?
should i be using "this" instead of "parama"? i don't understand whether or not to have any parameters for my function clicky.
var images = new Array()
images[0] = "bison"
images[1] = "frog"
function clicky(parama){
for (entry in images){
if (parama.attributes["name"].value === images[entry]){
$(parama).onClick(alert("This is a" + parama.attributes["name"].value));
} else {
$(parama).onClick(alert("dang it");
}
}
}
using sort of a combination of both your answers, I figured out a way to do it with a lot less code than I originally had. Check it out! (images all had classes of "pic")
$('.pic').click(function(){
alert("This is a " + this.getAttribute('alt'))
});
I'd recommend to use the title or alt attribute on images instead of a JS array - that's more SEO friendly and semantic. Not to mention that alt is required on images to make your HTML valid - DEMO
var images = document.getElementsByTagName("img");
for ( var i = 0, count = images.length; i < count; i++ ) {
images[i].addEventListener("click", function() {
alert( this.getAttribute("alt") );
});
}
UPDATE
if you open to use jQuery - DEMO
$("img").on("click", function() {
alert( $(this).prop("alt") );
});
You can use .click() but it's recommended to use .on() instead to attach different kind of event listeners to elements. jQuery also provides a shorthand for getting the properties - .prop()
Hopefully I can articulate this problem well. I'm working on a simple audio player in Titanium Desktop. The primary code I'm focused on right now is the following:
function pickMusicFolder (){
var win = Titanium.UI.getCurrentWindow();
win.openFolderChooserDialog(function(folderResponse) {
var file = Titanium.Filesystem.getFile(folderResponse[0]);
var listing = file.getDirectoryListing();
for (var i = 0; i < listing.length; i++) {
if (listing[i].isDirectory()) {
// if the listing is a directory, skip over it
$('#main').append('DIRECTORY: ' + listing[i].nativePath() +
'<br />');
// continue;
}
else {
// otherwise, print the filename of the file to the #main content window
var songOnList = listing[i].nativePath();
var songURL = songOnList.replace(/\\/g,"/");
$('#main ul').append('<li>' + songURL + '</li>');
}
}
});
};
function playSong(songURL){
var currentSong = Titanium.Media.createSound(songURL);
currentSong.play();
this.stopPlayback = stopPlayback;
function stopPlayback(currentSong){
currentSong.stop();
}
}
And then related HTML:
<input type="image" src="img/player_stop.png" name="stopPlayback" onClick="playSong.stopPlayback(songURL)" />
<input type="image" src="img/folder_add.png" name="pickMusicFolder" onClick="pickMusicFolder()" />
Now, both pickMusicFolder and playSong itself work properly. However, stopPlayback isn't working, I'm having a really hard time grasping how to deal with different functions for playing and stopping audio, since the code that's generating the clickable links is completely compartmentalized within the pickMusicFolder function, while the code for stopping playback is only attached to the one separate interface button.
I simply need access to the songURL variable between multiple functions in order to be able to perform operations on one solitary song while it's playing (or not). I'm avoiding resorting to global variables, as I find that to be sort of a cop out.
Anyone have any ideas? Any tips are much appreciated! (Oh, and please ignore my ugly code; was trying a bunch of hack-y solutions before posting.)
Very quickly solution is to store currentSong as property of function playSong:
function playSong(songURL){
var currentSong = Titanium.Media.createSound(songURL);
playSong.currentSong = currentSong; // store curernt playing song as property of function
currentSong.play();
}
playSong.stopPlayback = function() {
if (playSong.currentSong) {
playSong.currentSong.stop();
delete playSong.currentSong;
}
};
when song will be stopped just remove this property that would mean no song are playing now
You should use a closure. See below
var stopPlayback = function() { /* Placeholder, defined later */ };
var playSong = function(songURL){
var currentSong = Titanium.Media.createSound(songURL);
currentSong.play();
// Redefine stopSong in the parent scope
stopPlayback = function(){
// currentSong is available to this function via closure
currentSong.stop();
}
}
HTML Edit
<input type="image" src="img/player_stop.png" name="stopPlayback" onClick="stopPlayback()" />
As a side note, you should not be using HTML attributes to attach JavaScript events. This is a great series on event attachment, but what you really should read is this part. Also, every JS library provides methods for attaching events to make your life easier.