I have this script:
function downloadIt() {
var dataUri = "data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333"
var filename = "somedata.csv"
$("<a download='" + filename + "' href='" + dataUri + "'></a>")[0].click();
}
It works on Chrome but doesn't work on Firefox, without any error on the console. What is the cause and how to fix this?
Appending the element on the body solves the problem, just replace line 3 on the question above into this:
// store the element to a variable
var x = $("<a download='" + filename + "' href='" + dataUri + "'></a>");
// append to body
x.appendTo('body');
// click it (download)
x[0].click();
// remove from body
x.remove();
It seems that firefox won't execute the click event when the element not attached to the body
Related
I have a table being loaded from a JSON array, but my click event solution does not seem to work. As the loop is cycled through, I add a click event to each listener to each of the new added divs.
document.getElementById(i.toString()).addEventListener("click", function(event)
{
console.log(event);
});
The issue is only the last element responds to the clicks.
My code is available on pastebin
This is happening because of the way you are currently trying to add new elements to your html.
Replace this (what you are currently doing):
nw.innerHTML = nw.innerHTML + "<div class='" + nodeType + "' id='" + i + "'><div class='nodeName'>" + json[i][0] + "</div></div>";
With this:
var div = document.createElement("div");
div.setAttribute("class", nodeType);
div.setAttribute("id", i.toString());
div.innerHTML = '<div class="nodeName">' + json[i][0] + '</div>';
nw.appendChild(div);
Here's a fiddle that shows a simpler version of this working.
In my opinion,
nw.innerHTML = nw.innerHTML + "<div class='" + nodeType + "' id='" + i + "'><div class='nodeName'>" + json[i][0] + "</div></div>";
this code override previous nw's elements and their event listeners too.
Therefore, instead of using innerHTML, try to use document.createElement("div") and append it to nw using appendChild(). It works in my test.
I have a snippet of my jQuery code;
$('#elements').on('click', '.items', function () {
var content, id, tag;
tag = this.tagName;
id = $('#' + this.id);
content = id.html();
switch (tag.substr(0, 1)) {
case "P":
id.html("<textarea id='" + this.id + "In' class='" + tag + "In' type='text'>" + content + "</textarea>");
break;
case "H":
id.html("<input id='" + this.id + "In' class='" + tag + "In' value='" + content + "' >");
break;
}
});
The purpose of this is when I click on a paragraph tag, it will add a text area inside of the paragraph tag (with the content inside it ready for editing). When I click a heading tag, it will create an 'input' tag with the content inside it for editing.
Unfortunately, when i click twice on the paragraph, it adds a text area with the content inside it as it should but on the second click it adds another text area inside of that, now the 'content' of the textarea is: <textarea id="2In" class="PIn" type="text">Paragraph one. and with every click it adds: <textarea id="2In" class="PIn" type="text">
I understand this is happening as it should given the code but I want to stop the click event on that specific ID (this.id) but keep the click event active on the other elements with the class '.items'.
**Additionally: **
I'm sure this is bad practice to approach this by creating the editiable tags inside of the old ones so if anyone has a better approach be sure to let me know.
Many thanks,
Mike
I'd probably solve it by adding a :not(.clicked) to the selector, and adding that class when you add the input. E.g.:
$('#elements').on('click', '.items:not(.clicked)', function () {
$(this).addClass("clicked");
// ...your current handling...
});
But you could solve it by checking for the existence of the field, provided the input or textarea you're adding is the only one the paragraph will have:
$('#elements').on('click', '.items', function () {
if (!$(this).find("input, textarea")[0]) {
// ...your current handling...
}
});
Or actually jQuery extends CSS to provide :has and to allow :not to have more complex contents, so in theory this would work:
$('#elements').on('click', '.items:not(:has(input)):not(:has(textarea))', function () {
// ...your current handling...
});
...but that selector is getting a bit unwieldy...
What about using .one?
By using one, the click event can only be triggered once. Here's an example.
$('#elements > *').one('click', function () {
var content, id, tag;
tag = this.tagName;
id = $('#' + this.id);
content = id.html();
switch (tag.substr(0, 1)) {
case "P":
id.html("<textarea id='" + this.id + "In' class='" + tag + "In' type='text'>" + content + "</textarea>");
break;
case "H":
id.html("<input id='" + this.id + "In' class='" + tag + "In' value='" + content + "' >");
break;
}
});
But it seems you are trying to do something like allowing a user to edit text and saving the new input. I'd advise using a combination of contenteditable and localStorage.
In a Jquery Mobile application I have a listview that calls a function wen clicked:
Go home
When a row link as above is clicked it calls this function:
function OnSaveOrDelete(acID){
if (localStorage.Favourites.indexOf("/" + acID + "/") >= 0)
{
alert('Ac Removed #' + acID);
$(this).prop('data-icon','delete');
var tmp = localStorage.Favourites;
localStorage.Favourites = tmp.replace("/" + acID + "/", "");
}
else{
alert('New Ac Saved #' + acID);
localStorage.Favourites += "/" + acID + "/";
}
}
When the existing item isnt in localStorage I add it, and then need to change the icon / data-icon attribute. As is shown in my code above, Im trying to do this via:
$(this).prop('data-icon','delete');
Ive also tried:
$(this).attr('data-icon','delete');
But neither of these work. Im assuming this is because '$(this)' isnt defined.
How can I updated my code to do what I need?
You can pass this in:
onclick="OnSaveOrDelete(' + item.ID + ', this)"
function OnSaveOrDelete(acID, obj) {
//$(obj) refers to the clicked element
$(obj).prop('data-icon','delete');
}
I have a JavaScript function:
function addTool(id, text, tool, pic) {
var container = getById('infobox');
var origimg = getById('tempimg').src;
container.innerHTML += "<div id='" + id + "' class='toolText'>" + text + "<br><img class='toolImg' src='img/tools/" + tool + "'></div>";
getById(id).setAttribute('onMouseOver', "mOver('"+ id +"', '" + pic + "');");
getById(id).setAttribute('onMouseOut', "mOut('"+ id +"', '" + origimg + "');");
getById(id).setAttribute('href', 'javascript:mClick(id);');
}
Which generates several divs, using this code:
addTool("1p", "Bar", "tool1.jpg", 'img/p&g-part-2_skiss1-2.jpg');
addTool("2p", "Tube", "tool1.jpg", 'img/p&g-part-2_skiss1-2.jpg');
addTool("3p", "Rotating", "tool1.jpg", 'img/p&g-part-2_skiss1-2.jpg');
The mouse events work fine in all major browsers except IE. It seems that all divs except the last will have the mouse event in lowercase which will have the mouse event exactly as written, with upper case letters.
All mouse events will fire except for the last div, even if I write onmouseover instead of say ONmouseOVER, which works fine on all except the last.
Do not use setAttribute to add events. Use attachEventListener/addEvent
The problem you have is adding the elements to the div. You are basically wiping it away each time when you are adding the new elements. That is bad. You should be using appendChild to add new content to the div.
Basic idea:
function attachEvent(elem, eventName, fn) {
if ( elem.attachEvent ) {
elem.attachEvent( 'on' + eventName, fn);
} else {
elem.addEventListener( eventName, fn, false );
}
}
function addTool(text, message) {
var container = document.getElementById('infobox');
var newTool = document.createElement("a");
newTool.innerHTML = text;
newTool.href="#";
var myClickFnc = function(e) {
alert(message);
return false;
}
attachEvent(newTool, "click", myClickFnc);
container.appendChild(newTool);
}
addTool("cat","meow");
addTool("dog","bark");
addTool("pig","oink");
running example
Just as #epascarello pointed out, it seems that the setAttribute was the culprit, so I resolved it by setting the events in inline, such as this:
function addTool(id, text, tool, pic) {
var container = getById('infobox');
var origimg = getById('tempimg').src;
container.innerHTML += "<div id='" + id + "' class='toolText'" +
"onmouseover=\"mOver('"+ id +"', '" + pic + "');\" " +
"onmouseout=\"mOut('"+ id +"', '" + origimg + "');\" " +
"onclick=\"mClick(id);\"" +
">" + text + "<br><img class='toolImg' src='img/tools/" + tool + "'></div>";
}
Which worked just fine in all browsers, including IE.
You could do this part with JQuery:
$("#"+ id).mouseover(function() {
mOver('"+ id +"', '" + pic + "');
});
You can even take this a lot further:
https://stackoverflow.com/a/4158203/190596
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
var newvalue = '<img class="youtube_replace youtube_canvas" data-code="Wn-_MyJV37E" src="http://img.youtube.com/vi/Wn-_MyJV37E/0.jpg" />';
var abc = $('<div>' + newvalue + '</div>').find('*').each(function() {
}).html();
alert(abc);
</script>
I want abc to equal "newvalue". But in my current code, abc is empty. Why?
This is what I truly want to do, but for example purposes, I left this blank above:
var whitelist = ['a','div','img', 'span'];
var abc = $('<div>' + newvalue + '</div>').find('*').each(function() {
if($.inArray(this.nodeName.toLowerCase(), whitelist)==-1) {
$(this).remove();
}
}).html(); //abc is now sanitized!!!
Breaking it down:
var abc = $('<div>' + newvalue + '</div>') //Creates a div with an img element inside it
.find('*') //retrieves the img element
.each(function() {}) //iterates over the jQuery set (only one img element)
.html(); //returns the HTML serialization of the img element
//which has no contents, so it is an empty string
You could call .parent().html(), which would retrieve the contents of the div you created.
In your second example, you would want .end().html() which would pop the internal jQuery stack and get you back to the top-most div.
The issue here is that when you do:
var abc = $('<div>' + newvalue + '</div>').find('*')
your jQuery object holds the img element, not the div element. So when you're calling .html(), you're getting the inner HTML of the image - which of course doesn't exist.
var abc = $('<div>' + newvalue + '</div>')
.find('*')
.each(function() {
// stuff
})
.parent().html();
(but #Dennis got there first :). )
Do it like that (if you insist on getting HTML of element you just generated):
var abc = $('<div>' + newvalue + '</div>').html();
The problem is just incorrect mixing of different jQuery functions and callbacks.
EDIT:
The problem you have is that with find('*') you retrieve all the <img> tags (actually: one <img> tag) within <div>, but <img> tags have no HTML inside them (they have no other tags inside).
If you shorten your code to this:
var abc = $('<div>' + newvalue + '</div>').find('*').each(function() {
/* your JS code here */
}).parent().html();
you will actually receive HTML of the whole <img> tag.