Bookmarklet Javascript Array Leak? - javascript

For my project, I am creating a bookmarklet which gives users tag options. I am passing an array in of the user's top tags and looping in javascript to generate all the options.
The problem is that the array is picking up rogue functions from the underlying page that are somehow making it into the array and as you can see in the image below: screwing up everything.
I've tried manually removing these by taking off the blocks by string length - but I discovered that in some cases (as in the case of wired magazine articles) the rogue functions has an open script tag which I am removing and thus breaking the bookmarklet.
Here is the code where I build the javascript array:
<?php foreach ($default_aspects as $aspect_id => $aspect_display) { ?>
default_aspects['<?php echo $aspect_id; ?>'] = '<?php echo $aspect_display; ?>';
<?php } ?>
And here is the code where I print the array out:
html += '<div id="bml_category___" style="display:none; opacity:0;">';
for (var i in default_aspects) {
html += '<div class="bml_category_block___">' + default_aspects[i] + '</div>';
};
You can see what this looks like (and the problem) here:
http://cl.ly/0W1Y0B0U1S210L1h2y2n
I guess there is some sort of leak?

That page is probably using a JavaScript framework, which modifies native objects. Use the hasOwnProperty method to filter out these methods.
I use Object.hasOwnProperty.call(default_aspects, i) instead of default_aspects.hasOwnProperty(i), to make sure that a property named hasOwnProperty does not break your code.
html += '<div id="bml_category___" style="display:none; opacity:0;">';
for (var i in default_aspects) {
if (Object.hasOwnProperty.call(default_aspects, i)) {
html += '<div class="bml_category_block___">' + default_aspects[i] + '</div>';
}
};
If you've got a modern browser, it's easier to use Object.keys():
var keys = Object.keys(default_aspects);
for (var i=0; i<keys.length; i++) {
html += '<div class="bml_category_block___">' + default_aspects[i] + '</div>'
}
Or, using Array.forEach:
Object.keys(default_aspects).forEach(function(key) {
html += '<div class="bml_category_block___">' + default_aspects[key] + '</div>'
});

Related

addeventlistener to indexed element

I have a list of elements. However, the length of this list varies between trials. For example, sometimes there are 6 elements and sometimes there are 8. The exact number is detailed in an external metadata.
To display this variable list, I've written:
var html = '';
html += '<div id="button' + ind + '" class="buttons">';
html += '<p>' + name + '</p></div>';
display_element.innerHTML = html;
If I were to 'inspect' the elements in my browser, they would appear to have IDs of button0.buttons, button1.buttons, etc.
Now I am trying to attach event listeners to each element but my code is not working so far. Different forms of broken code below:
document.getElementById("button' + ind + '").addEventListener("click", foo);
$("#button' + ind + '").click(foo);
document.getElementById("button").addEventListener("click", foo);
$("#button").click(foo);
Any help would be very appreciated! Thanks.
You wrong at concat string update it as
document.getElementById("button" + ind).addEventListener("click", foo);
var html = '';
var ind = 1;
var display_element = document.getElementById("test");
html += '<div id="button' + ind + '" class="buttons">';
html += '<p>' + name + '</p></div>';
display_element.innerHTML = html;
document.getElementById("button" + ind).addEventListener("click", foo);
function foo(){
alert('click');
}
<div id="test"></div>
Use "document.getElementsByClassName" get all botton elements then foreach to add click function.
document.getElementsByClassName('buttons').map( element => { element.addEventListener("click", foo) })
To answer the question of why neither of those uses of document.getElementById() are working for you, you are mixing your quotes incorrectly. "button' + ind '" evaluates to exactly that, rather than evaluating to "button0", "button1", etc. To make your code more readable, and to avoid similar quote mixing issues, I would recommend looking into template literals https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
With modern JS if you want to execute the same function you won't require to add an id to each button.
Just use the class added to the buttons like this:
document.querySelectorAll('.buttons').forEach(button => {
button.addEventListener('click',foo);
});
Then use the event parameter in that function to get the target node & execute whatever you want. You can also add data attributes in those buttons to use while executing that function.

Jquery for loop not working

I am currently working on a site that does not allow me to edit some of the html content on each of the pages because it is part of a template. I am using Jquery to insert the divs on each page so that i can style them for later. I am currently stuck on this piece of code and cannot get it to work for me. Any suggestions would be greatly appreciated.
jQuery(document).ready(function() {
var URLarray = {
"/100.htm", "<div class='Plugins-div'>Volusion Plugins</div>";
"/101.htm", "<div class='Pricing-div'>Pricing</div>";
"/102.htm", "<div class='Services-div'>Services</div>";
"/103.htm", "<div class='Contact-div'>Contact Us</div>";
}
jQuery.each(URLarray, function(key, value) {
if (location.pathname.indexOf(key) > -1) {
pageHTML = URLarray
jQuery('div#navheader').after('<div id="top-banner" class="col-xs-12"><div class="text-center container">' + pageHTML + '</div></div>');
}
}
});
});
Your code doesn't work (and causes an exception, check your console), because it is full of syntax errors. Object literal that looks like and array (and is named as an array), but has semicolons inside, misplaced closing curly braces, assignment of object where a string was probably meant to be used...
It's not entirely clear what you want to do, but this will probably work (and is at least valid code):
var URLarray = {
"/100.htm": "<div class='Plugins-div'>Volusion Plugins</div>",
"/101.htm": "<div class='Pricing-div'>Pricing</div>",
"/102.htm": "<div class='Services-div'>Services</div>",
"/103.htm": "<div class='Contact-div'>Contact Us</div>"
}
jQuery.each(URLarray, function(key, value) {
if (location.pathname.indexOf(key) > -1) {
jQuery('div#navheader').after('<div id="top-banner" class="col-xs-12"><div class="text-center container">' + value + '</div></div>');
}
});

How do dynamically create HTML via JavaScript using property names in Struts?

Here's my problem:
I have a JavaScript function inside of a JSP that looks like this:
<script type="text/javascript">
function generateTable()
{
var temp = '';
temp = temp + '<logic:iterate name="dataList" id="dto" indexId="dtoIndex" >';
temp = temp + '<logic:equal name="dtoIndex" value="0">';
temp = temp + '<thead>';
temp = temp + '<tr class="topexpression7"></tr></thead><tbody></logic:equal>';
temp = temp + '<tr>';
var propertyArray = new Array('"title"','"jDate"','"employeeId"','"employeeName"');
var arrayLength = propertyArray.length;
var html = '';
var i=0;
for (i=0; i<arrayLength; i++)
{
if (i == 2)
{
// left
html = html + '<logic:present name="dto" property=' + propertyArray[i] + '><td class="left"> <bean:write name="dto" property=' + propertyArray[i] + '/></td></logic:present>';
}
else if (i == 3)
{
// Only applies to this property
html = html + '<logic:present name="dto" property="employeeName">';
html = html + '<td class="left" style="white-space:nowrap;"> ';
html = html + '<nobr><bean:write name="dto" property="employeeName"/>';
html = html + '</nobr></td></logic:present>';
}
else
{
// center
html = html + '<logic:present name="dto" property=' + propertyArray[i] + '><td class="center"> <bean:write name="dto" property=' + propertyArray[i] + '/></td></logic:present>';
}
}
temp = temp + html + '</logic:iterate></tbody>';
// Write out the HTML
document.writeln(temp);
}
</script>
If I hard code the property like where (i == 3), it works fine. Renders as expected.
But by trying to parse a string dynamically (where i <> 3), the string var "html" is null every time. Admittedly, my JavaScript is average at best. I'm sure it's an easy fix, but darned if I can figure it out!
P.S. Long story as to why I'm going this route, and I'll spare you the story (you're welcome). I just want to know why the variable propertyArray[i] isn't working.
The JSP is rendered on the server and JavaScript on the client browser, but to render properly the JSP tags should be well formed, i.e. have all necessary attributes with valid values, begin and close tag, etc. But not all your tags are valid. First your JSP compiled on the server and it can't process the bad JSP tags. When i != 3 you have that bad JSP tags. When the JSP is compiled the JavaScript code is used like a content, it has less meaning for the JSP compiler because it is looking on the tags that correspond a JSP syntax. Looking by the eyes of the JSP compiler you'll see the logic:present tag has attribute property but has not a value because propertyArray[i] is not evaluated as a JSP expression, it simply breaks the tag boundary. So the Tag is not compiled properly. If you place JSP tags into JavaScript code make sure they're consistent.
propertyArray[i] is working; it's the rest of it that isn't (and won't).
JSP tags don't execute on the client side; HTML generated in JavaScript must not include JSP tags (unless you don't care if they don't run). Instead, the JavaScript itself must be generated using tags on the server side before it's sent to the browser.
In this case, however, it might be best to just render HTML returned from an Ajax request, though, depending on what you're really trying to do, or create it all in JSP (not JS), etc.

Show/Hide Multiple DIVs in a variable

I am using nested for loops to generate multiple instances of a table with details of projects; under which I wish to have a show/hide button that will give a short description of each project at a high level.
I am tring to manipulate code I found here: https://forums.digitalpoint.com/threads/javascript-to-show-hide-tables.1009918/
The following code produces a "Show/Hide" link that does not work on my page (see screenshot). Am I missing something?
FYI - "Separate" in the code below is an array containing unique project references to facilitate the separation of the tables per project. So where Separate contains 4 elements, there should be 4 projects, 4 tables, and so on.
Many Thanks,
Karl
function showhide(id){
if (document.getElementById){
obj = document.getElementById(id);
if (obj.style.display == "none"){
obj.style.display = "";
} else {
obj.style.display = "none";
}
}
}
for(i in Separate){
DescID[i] = "DescID"+i;}
var Table = "";
for(i in Separate){
Table += "<table id='dashboard' summary='Project Dashboard'>";
Table += "<THEAD>";
Table += "<TR><TH scope='col' colspan=4><B>"+ Separate[i] +"</B></TH></TR>";
Table += "<TR><TH scope='col'>Task Names</TH><TH scope='col'>Task Summary</TH><TH scope='col'>RAG</TH><TH scope='col'>Timeline</TH></TR></THEAD>";
Table += "</THEAD>";
Table += "<TBODY>";
for(j in Project){
if(Project[j] == Separate[i]){
Table += "<TR><TD title='" + Comments[j] + "'>"+ Task[j] +"</TD><TD>"+ Summary[j] +"</TD><TD><img src='/images/RAG/" + RAG[j] + "'></TD><TD>"+ DateType[j] +" "+ Status[j].substring(0,10) +"</TD></TR>";
}
}
Table += "</TBODY>";
Table += "</table>";
Table += "<a onclick ='javascript:ShowHide('" + DescID[i] + "')' href='javascript:;' >Show/Hide Project Description</a>";
Table += "<div class='mid' id='" + DescID[i] + "' style='DISPLAY: none' >Placeholder for Project Description</div>";
Table += "<BR>";
}
You're missing the fact that HTML tag "id"s (e.g., for your Projects' tables) should be unique, otherwise "getElementById" won't work. Now they're all set to "dashboard". At the very least you could add the index, and make them "dashboard1", "dashboard2", etc...
Also, IIRC, a better opposite for 'display = "none";' is 'display = "inline";', instead of 'display = "";'. Although this needs more testing on different browsers to select the best option.
Thirdly, your JavaScript call within the onclick events use single quotes BOTH for the attribute value definition (surrounding the JS call) and for the string parameter (the id to show/hide)... That's not valid syntax, and you need one of these two use-cases to be double-quotes.
And the other main problem your code has is as mikez302 already spotted: "Javascript function names are case-sensitive. Your function is called showhide but you are trying to call ShowHide." Correcting these two issues (the quotes and the function name) will allow the code to work, i've just tested it. :)

jquery: "Exception thrown and not caught" in IE8, but works in other browsers

My code works fine in other browsers, but in IE8 I get "error on page" - and when I click that it says:
"Exception thrown and not caught Line: 16 Char: 15120 Code: 0
URI: http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"
I tried linking to jquery.js (rather than jquery.min.js) and to 1.5.1/jquery.min.js,
but problem still remains.
Can someone correct/improve my code for me, or guide me as to where to look. Thanks
<script type="text/javascript">
function fbFetch()
{
var token = "<<tag_removed>>&expires_in=0";
//Set Url of JSON data from the facebook graph api. make sure callback is set with a '?' to overcome the cross domain problems with JSON
var url = "https://graph.facebook.com/<<ID_REMOVED>>?&callback=?&access_token=" + token;
//Use jQuery getJSON method to fetch the data from the url and then create our unordered list with the relevant data.
$.getJSON(url, function(json)
{
json.data = json.data.reverse(); // need to reverse it as FB outputs it as earliest last!
var html = "<div class='facebook'>";
//loop through and within data array's retrieve the message variable.
$.each(json.data, function(i, fb)
{
html += "<div class='n' >" + fb.name;
html += "<div class='t'>" + (dateFormat(fb.start_time, "ddd, mmm dS, yyyy")) + " at " + (dateFormat(fb.start_time, "h:MMtt")) + "</div >";
html += "<div class='l'>" + fb.location + "</div >";
html += '<div class="i"><a target="_blank" title="opens in NEW window" href="https://www.facebook.com/pages/<<id_removed>>#!/event.php?eid=' + fb.id + '" >more info...</a></div>';
html += "</div >";
}
);
html += "</div>";
//A little animation once fetched
$('.facebookfeed').animate({opacity: 0}, 500, function(){
$('.facebookfeed').html(html);
});
$('.facebookfeed').animate({opacity: 1}, 500);
});
};
Does the code do the job in IE8 or does it break? The reason I ask is because if it works as expected you could just wrap it in a try{ } catch{ \\do nothing } block and put it down to another thing IE is rubbish at.
You may be better off creating an object for the creation of the facebook div. Something like...
var html = $('<div />');
html.attr('class', 'facebook');
Then in your each loop you can do this...
$('<div />').attr('class', 'n').append(fb.name).appendTo(html);
$('<div />').attr('class', 't').append etc...
Then append html to the facebookfeed object
Doing this may remove the scope for error when using single quotes and double quotes when joining strings together, which in turn may solve your issue in IE8
$('.facebookfeed').fadeOut(500, function(){
$(this).append(html).fadeIn(500);
});
Hope this helps!
UPDATE
The append method is used to add stuff to a jquery object. For more info see here
So to surround the div's as you mentioned in the comments you would do something like this...
var nDiv = $('<div />').attr('class', 'n').append(fb.name);
$('<div />').attr('class', 't').append(fb.somethingElse).appendTo(nDiv);
// etc
And then you would need to append that to the html div like so...
html.append(nDiv);
So that would give you
<div class="facebook">
<div class="n">
value of fb.name
<div class="t">
value of fb.somethingElse
</div>
</div>
</div>
So what you have done is created a new jquery object and appended to that, then appended that to the html object which you have then appended to the facebookfeed div. Confusing huh?!

Categories

Resources