I wrote some code that generates a bunch of html-elements based on a Json-Object. I add it to the page by JQuerys .append() and .after().
It does work perfectly often, but sometimes the outer loop is only executed once and stops at $( '#'+inputname ).entityselector().
function addlinks(qid, prop) {
html="<fieldset id=\"quickpresets\">" +
"<legend>Quick Presets (" + prop.name + ")</legend></fieldset>";
$('.wikibase-statementgrouplistview').first().after( html );
for( var p = 0; p < prop.defaults.length; p++ ) {
pid=prop.defaults[p].pid;
pname=prop.defaults[p].name;
pvalues=prop.defaults[p].values;
inputname="input"+pname;
pclass="addstatement";
if($('#P'+pid).find(".wikibase-snakview-value a").length !== 0) {
pclass += " disabled";
}
str="<p class='"+pclass+"'>Add "+pname+":";
for( var i = 0; i < pvalues.length; i++) {
toqid=pvalues[i].qid;
toname=pvalues[i].name;
str += " <a href='javascript:void(0);' onclick=\""+
"additemstatement("+qid+","+pid+",'"+pname+"',"+ toqid +",'" + toname+ "')\">" + toname+ "</a>"+
" ∙";
}
str += "<span class=\"quickpresetsinput\"><input id='"+inputname+"'/> ";
str += "<a href=\'javascript:void(0);\' onclick=\""+
"onselectitem("+qid+","+pid+",'"+pname+"','"+ inputname +"')\">✔</a>";
str += "</span></p>";
$('#quickpresets').append( str );
input = $( '#'+inputname ).entityselector( {
url: 'https://www.wikidata.org/w/api.php',
language: mw.config.get('wgUserLanguage')
} );
}
}
How do I fix this issue? And what other things are there that I should do to improve this ugly code?
Updates:
I get the following error in the console:
TypeError: $(...).entityselector is not a function [Weitere
Informationen]
The full code can be found here.
I have to use ES5.
The data is always the same ("hard coded") JSON.
See below for the better readable version of Roamer-1888 – which still causes the same bug.
It's not apparent in the code why .entityselector() should throw on some occasions. The most likely reason is that you are trying to invoke the plugin before it is loaded.
In an attempt to fix the issue, you might try initialising all the inputs in one hit instead of individually in the loop.
Also, the code would be made more readable by attaching a fairly simple fragment to the DOM, then immediately adding links and attaching event handlers with jQuery.
Here it is the way I would write it (with a bunch of tidying up) :
function addlinks(qid, prop) {
// compose and intert fieldset.
var $fieldset = $("<fieldset><legend>Quick Presets (" + prop.name + ")</legend></fieldset>")
.insertAfter($('.wikibase-statementgrouplistview').first());
// loop through prop.defaults
prop.defaults.forEach(function(dflt) {
// compose and append a basic fragment ...
var $fragment = $("<p class='addstatement'>Add " + dflt.pname + ":<span class=\"links\"></span>"
+ "<span class=\"quickpresetsinput\"><input /> ✔</span></p>")
.appendTo($fieldset);
// ... then allow jQuery to augment the appended fragment :
// i) conditionally addClass('disabled')
if($('#P' + dflt.pid).find(".wikibase-snakview-value a").length !== 0) {
$fragment.addClass('disabled');
}
// ii) loop through dflt.values and add links.
dflt.values.forEach(function(val) {
$("<span> ∙</span>")
.appendTo($fragment.find('span.links'))
.find('a')
.text(val.name)
.on('click', function(e) {
e.preventDefault();
additemstatement(qid, dflt.pid, dflt.pname, val.qid, val.name);
});
});
// iii) attach click handlers to the quickpresets inputs
$fragment.find('.quickpresetsinput').find('a').on('click', function(e) {
e.preventDefault();
var selection = $(this).prev('input').data('entityselector').selectedEntity();
additemstatement(qid, dflt.pid, dflt.pname, selection.id.substring(1), selection.label);
});
});
// invoke .entityselector() on all the quickpresets inputs in one hit
$('.quickpresetsinput input').entityselector({
'url': 'https://www.wikidata.org/w/api.php',
'language': mw.config.get('wgUserLanguage')
});
}
untested except for syntax
That's certainly tidier though without a proper understanding of the original issue, .entityselector() may still throw.
Related
I'm trying to create a chrome extension. I had a problem with the affectation of event for the new element that i append to the dom of site with content. Js
If I add an event to an element' 'for example class' exist already in the page, it works correctly. Just for my new appended element((in the code iadded a button ,the event is just an alert to test))
function tst() {
myclass = $("._3hg-._42ft");
myclass = myclass.not(".supp");
myclass.addClass("supp");
var patt = /https:\/\/(.)*\.facebook\.com\/(.)*\/(posts|photos|videos)\/(\w|\.|\d)*/g;
for (i = 0; i < myclass.length; i++) {
result = patt.exec(myclass[i]);
myclass.append('<button class="fact" id=' + result[0] + ' style="position: absolute;">fact</button>');
};
/* this is a simple event*/
/***********************/
$(".fact").on('click', function() {
alert("no event work ");
});
Making somewhat broad assumption here in my answer that it is JavaScript/jQuery related and is NOT an extension...or is so still in that context.
You need to attach the event to the container here perhaps for the dynamically created elements. Lots of global stuff, suggested to not do that, updated there.
Appends a lot of buttons perhaps? might need to only hit DOM once but left as-is in this isolated function.
function tst() {
let myclass = $("._3hg-._42ft")
.not(".supp");
myclass.addClass("supp");
//let result = {};
var patt = /https:\/\/(.)*\.facebook\.com\/(.)*\/(posts|photos|videos)\/(\w|\.|\d)*/g;
var i = 0; //avoid global
for (i; i < myclass.length; i++) {
// broad assumption of the returned value from patt.exec() here
// not even sure why it needs an id, have a class, use for css
let result = patt.exec(myclass[i]);
myclass.append('<button class="fact" id="' + result[0] + '">fact</button>');
}
/* attache event to pre-existing element */
/***********************/
myclass.on('click', ".fact", function() {
alert("event works");
});
}
button.fact {
position: absolute;
}
I have a question about "onclick" function in JavaScript. Here I have a div "InfoBar"
<div id="InfoBar"><br>
and two for loop
var src = new Array();
for(var i = 0; i < 2; i++){
src.push("el1","el2");
}
for(var j = 0; j < 2; j++){
doesFileExist(src[j]);
}
and a doesFileExist() and klick function
function klick(el){
alert(el)
}
function doesFileExist(urlToFile){
document.getElementById('InfoBar').innerHTML += '<br>' + '<a id="css" onclick="klick(urlToFile)" href="#" title="'+urlToFile+'">' + "link1 : " + urlToFile + '</a>';
}
now I've added a "onclick" function in "a href".
if I click on "link1:el1", I want to display as alert "urlToFile" string.
But I doesn't work.
In "a href" title="'+urlToFile+'" it works perfect, but in "onclick" doesn't work.
Can anyone help me?
Thanks in advance.
You are generating an attribute. That gets converted back into a function but the scope is broken.
Don't use intrinsic event attributes.
Minimise use of globals
Avoid generating HTML by mashing strings together (at best it is hard to read, at worst you get this sort of issue)
Use standard DOM:
var container = document.getElementById('InfoBar');
container.innerHTML = ""; // Delete any existing content
container.appendChild(document.createElement('br'));
var anchor = document.createElement('a');
anchor.setAttribute('id', 'css'); // You are running this function is a loop and creating duplicate ids. Use a class instead.
anchor.addEventListener('click', function (event) {
klick(urlToFile); // the local variable urlToFile is still in scope
});
anchor.setAttribute('href', '#'); // Why are you linking to the top of the page? Use a <button>
anchor.setAttribute('title', urlToFile);
anchor.appendChild(document.createTextNode("link1 : " + urToFile));
container.appendChild(anchor);
Event handles assigned this way won't work. You have to use JavaScript event handles. Means, you must create a new 'a' element, then bind a click event to it, and then append it as a child to the parent node. All this stuff is very good described on the web out there.
I have a textbox that comma separated/delimited values are entered into which I have to make sure has unique entries. Solved that using Paul Irish's Duck Punching example #2 and tying it to onblur for that textbox.
The values entered into the textbox get broken out into a table. As the table can get very lengthy, I found Mottie's Tablesorter to work brilliantly.
The problem is, the the Duck Punching code is breaking the Tablesorter. The style for the Tablesorter is passed through just fine, but the table doesn't actually sort. BUT, when I comment out the Duck Punching code, Tablesorter miraculosly works.
My coding skills are not such that I can figure out why the two are conflicting. Any assistance would be much appreciated.
I haven't modified the Tablesorter code or added any special sorting elements to it...just following the very basic example right now. Here's the Duck Punching code which I've only modified to include the var for the textbox I need to have unique entries.
function ValidateTextBox1()
{
(function($){
var arr = document.getElementById("TextBox1").value.split(',');
var _old = $.unique;
$.unique = function(arr){
// do the default behavior only if we got an array of elements
if (!!arr[0].nodeType){
return _old.apply(this,arguments);
} else {
// reduce the array to contain no dupes via grep/inArray
return $.grep(arr,function(v,k){
return $.inArray(v,arr) === k;
});
}
};
})(jQuery);
}
The function above is in a separate js file which is called via onblur for TextBox1.
Then, I have a button which runs the following:
function GenerateTable()
{
var Entry1 = document.getElementById("TextBox1").value
var Entry2 = document.getElementById("TextBox2").value
var content = "<table id=myTable class=tablesorter ><thead><tr><th>Entry 1 Values</th><th>Entry 2 Value</th></tr></thead><tbody>"
var lines = Entry1.split(","),
i;
for (i = 0; i < lines.length; i++)
content += "<tr><td>" + (Entry1.split(",")[i]) + "</td><td>" + Entry2 + "</td></tr>";
content += "</tbody></table>"
$("#here_table").append(content);
$(function(){
$("#myTable").tablesorter({
theme: 'default'
});
});
}
The function will generate/append the table in a specific DIV.
If I leave in the validation code for TextBox1, the table will generate but isn't sortable (though it does manage to still pull the theme).
If I remove the validation code, the table will generate and is sortable.
The validateText box function above will not work as expected. In this case, "duck-punching" is not even necessary.
Here is how I would fix the script (demo):
HTML
<textarea id="textbox1">6,1,7,5,3,4,3,2,4</textarea><br>
<textarea id="textbox2">column 2</textarea><br>
<button>Build Table</button>
<div id="here_table"></div>
Script (requires jQuery 1.7+)
(function($) {
// bind to button
$(function () {
$('button').on('click', function () {
// disable button to prevent multiple updates
$(this).prop('disabled', true);
generateTable();
});
});
function unique(arr) {
return $.grep(arr, function (v, k) {
return $.inArray(v, arr) === k;
});
}
function generateTable() {
var i,
$wrap = $('#here_table'),
// get text box value, remove unwanted
// spaces/tabs/carriage returns & create an array
val = $('#textbox1').val().split(/\s*,\s*/),
// get unique values for Entry1
entry1 = unique( val ),
entry2 = $('#textbox2').val(),
content = "";
// build tbody rows
for (i = 0; i < entry1.length; i++) {
content += "<tr><td>" + (entry1[i] || '?') + "</td><td>" + entry2 + "</td></tr>";
}
// update or create table
if ($wrap.find('table').length) {
// table exists, just update the data
$wrap.find('tbody').remove();
$wrap.find('table')
.append(content)
.trigger('update');
} else {
// table doesn't exist, build it from scratch
$wrap
.html('<table id=myTable class=tablesorter><thead><tr>' +
'<th>Entry 1 Values</th>' +
'<th>Entry 2 Value</th>' +
'</tr></thead><tbody>' + content + '</tbody></table>')
.find('table')
.tablesorter({
theme: 'blue'
});
}
// enable the button to allow updating the table
$('button').prop('disabled', false);
}
})(jQuery);
I tried to add a few comments to make more clear what each step is doing. Please feel free to ask for any clarification.
im trying to loop through a JSON object via Jquery. For some reason its not looping right.. It seems to be looping all the way to the end, But I would like to get each individually property in my object. Im using a For(var in) loop which loops through my object correctly but its a bit off.. MyAny help would be glady appreciated.. thanks so much!!! I can provide a quick link to my website that has mock up of the code if needed..
Ive also added more code and html via elements that using ..Hint*** Theres more if - conditional statements that checks for sounds_like,sounds_price... The first JSON Object works with no problem, its the second JSON object that im using with the pop over thats causing me trouble
<div class="overlay-bg">
<div id= "overlay" class="overlay-content">
<p>This is a popup!</p>
<button class="close-btn">Close</button>
</div>
</div>
$.getJSON("php/music_data.php",function(data){
$.each(data,function(key,obj){
for(var prop in obj){
// console.log("Property: " + prop + " key:" + obj[prop]);
if(prop === "sounds_like"){
results_div = document.getElementById("results");
music_div_container = document.createElement("div");
music_div_container.id = "music_data_container";
music_div_container.innerHTML = "<div id=\"sounds_like\">" + "Sounds Like: " + obj["sounds_like"] +"</div>";
results_div.appendChild(music_div_container);
var pop_up = document.createElement("a");
pop_up.href = "#";
pop_up.className = "show-popup";
pop_up.innerHTML = "Click";
music_div_container.appendChild(pop_up);
default_photo = document.createElement('div');
}
if(prop === "sound_desc"){
var sound_desc = document.createElement("div");
sound_desc.innerHTML = "<div id=\"sounds_desc\">" + obj["sound_desc"] +"</div>";
music_div_container.appendChild(sound_desc);
}
$.getJSON("php/music_data.php",function(data){
$.each(data,function(idx,obj){
$.each(obj,function(key,value){
$(".show-popup").click(function(event){
event.preventDefault();
$(".overlay-bg").show();
if(key === "sounds_like"){
/***Should be Beyonce,Drake,Nicki Minaj***/
/*****But my console is showing Nicki Minaj*******/
$(".overlay-content").html(value);
}
if(value === "sounds_desc"){
/***Should be Smooth, Wu tang Forever, Barbie***/
/*****But my console is showing Barbie*******/
$(".overlay-content").html(value);
}
$('.close-btn').click(function(){
$('.overlay-bg').hide(); /*** hide the overlay ***/
});
$('.overlay-bg').click(function(){
$('.overlay-bg').hide();
});
$('.overlay-bg').click(function(){
return false;
})
});
});
})
});
JSON Object Below
[{"id":"39","sounds_like":"Beyonce","sound_name":"Dance 4 u.mp3","sound_desc":"Smooth","sound_genre":"R&B","sound_price":"N/A","photo_path":"\/admin_data\/uploaded_artist_photos\/","photo_name":"beyonce.jpg"},
{"id":"40","sounds_like":"Drake","sound_name":"Bottom.mp3","sound_desc":"Wu Tang Forever","sound_genre":"Rap","sound_price":"N/A","photo_path":"\/admin_data\/uploaded_artist_photos\/","photo_name":"drake.jpg"},
{"id":"41","sounds_like":"Nicki Minaj","sound_name":"RomanReloaded.mp3","sound_desc":"Barbie","sound_genre":"Rap","sound_price":"N/A","photo_path":"\/admin_data\/uploaded_artist_photos\/","photo_name":"nickiminaj.jpg"}
]
When you loop a complex object using a for var in loop, you will always have unexpected behaviors because of how this loop works.
To avoid such problems and if you need to use this type of loop, I recommend you do the following:
Example:
for (var i in obj) {
if (obj.hasOwnProperty(i)) { // this validates if prop belongs to obj
// do something
}
}
EDIT 1:
I'm not sure what you're trying to do but using jquery you can do the following:
$.getJSON("php/music_data.php", function (data) {
$.each(data, function (i, value) {
//alert(i + ": " + value.id);
alert(value.sounds_like);
// this will have Beyonce , Drake, Nicki Minaj
});
});
Another thing that does not seem right is that you're doing bind click event on each loop. Is it better to do this differently.
People on here are recommending that I use jQuery, but when I changed the code to jQuery and used .html() it is like it did nothing. I even removed half of the html code that needed to be added as someone suggested I was asking way to much of innerHTML and HTML.
In Simple task, all I want is for when a user click on the DIV that it runs the onClick event.
html += "<div onClick='loadnewsstory();' class='news'> this is a test story, for this test story we are not getting data from JSON</div>";
I have tried both
$("#activecontent").html(html);
document.getElementById("activecontent").innerHTML
The problem I have is relating to the following code.
function newsstories()
{
document.getElementById("activecontent").innerHTML = "<h1 class='newsheader'>Latest News</h1>";
xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","http://test.com/?uri=loadnews",false);
xmlhttp.send();
var newsreponse = JSON.parse(xmlhttp.responseText);
for (var i = 0, len = newsreponse.length; i < len; ++i) {
var news = newsreponse[i];
if(i % 2 == 0){
cssclass = "even";
}
else
{
cssclass = "odd";
}
// alert(news.featured_image);
document.getElementById("activecontent").innerHTML = document.getElementById("activecontent").innerHTML + "<div class='news " + cssclass + "'><div class='newstitle'><div class='newstitlecolor' id='news_"+ countstory+"'><a href='javascript:loadnewsstory();'>" + news.post_title + "</a></div></div><div class='base' style='background: url('" + news.featured_image + "');'><img src='" + news.featured_image + "' style='width:100%; height:100%;'/></div></div>";
}
}
you will see in this area i have a link
<a href='javascript:loadnewsstory();'>" + news.post_title + "</a>
it suppose to fire
function loadnewsstory()
{
navigator.notification.alert(device.uuid);
}
but I am not getting that fire.
Yes this is a web app for iOS and Cordova but I believe this is a javascript issue.
Don't use +=, as it is used in an improper instance and returns an "unexpected token" error because var html was not previously equal to anything. I removed it and it appeared to fix the problem. Fiddle
If you must use += set var html = $("#activecontent").html(), then you may afterwards use += when you re-define the variable (Fiddle 2)
If your structure looks like
html
<div id="activecontent">
<div class='news'>Story 1</div>
<div class='news'>Story 2</div>
</div>
and you want each div.news to by dynamic and clickable, you could do that like this with jQuery
javascript
$(function(){
$("#activecontent").on('click', '.news', function(){
//You clicked the div
console.log( 'Clicked', $(this) );
});
});
And if you want to append divs to your #activecontent with an ajax request. Let's assume your JSON looks like
json
[
{ "id": 1, "content": "My first story" },
{ "id": 2, "content": "Another one" },
{ "id": 3, "content": "Last story" }
]
Your javascript to load that could look like
javascript
$.getJSON( "http://url_of_json.json", function(result){
for(var i in result){
$("#activecontent").append( $("<div>").addClass('news').html(result[i].content) );
}
});
alternative javascript for the ajax which is faster on the DOM
$.getJSON( "http://url_of_json.json", function(result){
var newHtml = "";
for(var i in result){
newHtml += "<div class='news'>" + result[i].content + "</div>";
}
$("#activecontent").append( newHtml );
// Or $("#activecontent").html( newHtml );
// if you want to replace what the page loaded with
});
Now to explain. The first piece of javascript with the .on, what were doing there is binding an event listener to your parent div, #activecontent. We do that because it will always exist in your page. You will be adding and maybe removing divs from that container based on your AJAX call, so instead of having to bind a click (or inline some javascript for every div), you can bind once to the parent, and then delegate that click to '.news'. You can alternatively bind the click to each new div, but delegating is cleaner.
As for the part about loading the JSON and writing it. If you are going to add some stuff to a node's innerHTML, the jQuery way is to use .append(). It's just a shortcut to something like
//vanilla js way
var e = document.getElementById('myThing');
e.innerHTML = e.innerHTML + "Thing I want to append";
// vs jQuery way
$("#myThing").append("Thing I want to append");
//To continue this example, to replace your myThing's html
//vanilla
e.innerHTML = "my new html";
//jQuery
$("#myThing").html("my new html");
Hopefully this clears things up for you. If you are just jumping into jQuery, know that it's not always that it's faster to write than the vanilla javascript, but rather that when you do something like ..html('new stuff');, it's going to use a method that works best with all browsers. So if there's some rogue version of IE out there than wants to use .innerHTMLmsIsNeat instead of .innerHTML, jQuery will sort that for you.