I've been working on some JavaScript for a little while, and one of the most frustrating things so far is that it seems my JavaScript is refusing to load if Back, Forward or Hard Refresh are used. Considering my website is built on static HTML pages generated repeatedly through a MySQL database connection, I had to manually add a moderator menu, but it seems to sometimes just refuse to appear?
if (getCookie('opt23') !== '1') {
var ajaxtwo = new XMLHttpRequest();
var wrap = '';
if (document.getElementById("firstpage") !== null) {
wrap = document.getElementById("firstpage");
} else {
wrap = document.getElementById("firstpageinside");
}
var main = document.createElement("div");
main.setAttribute("id","modmenu");
wrap.appendChild(main);
ajaxtwo.onreadystatechange = function() {
var reports = (ajaxtwo.responseText === '0' ? 'flagblue.png' : 'flag.png');
reportcount.innerHTML = '<img src=' + ku_cgipath + '/css/images/mods/' + reports + ' style="height:15px;' +
'width:15px;" />' + ajaxtwo.responseText;
};
ajaxtwo.open("GET",ku_cgipath + "/manage_page.php?action=reports&count",true);
ajaxtwo.send();
var threadid = document.getElementsByName("replythread");
// taking the initiative of togglePassword, this makes things less needlessly lengthy.
main.innerHTML =
'<h2>Quick Mod</h2>' +
'<input type="hidden" name="threadid" value="' + threadid[0].value +'" />' +
'<label for="modaction">Action </label><select id="action" name="modaction">' +
'<option value=""><none></option>' +
'<option value="delpost">Delete posts</option>' +
'<option value="rebuildone">Rebuild board</option>' +
'<option value="bans">View Bans</option>' +
'<option value="appeals">View Appeals</option>' +
'<option value="stickypost">Sticky Thread</option>' +
'<option value="unstickypost">Unsticky Thread</option>' +
'<option value=lockpost>Lock Thread</option>' +
'<option value="unlockpost">Unlock Thread</option>' +
'<option value="bump">Instant Bump</option>' +
'<option value="viewthread">Switch to Moderator View</option>' +
'</select>' +
'<input type="submit" name="submit" value="Submit">' +
'<a href="' + ku_cgipath + '/manage_page.php?action=reports" target="_blank">' +
'<span id="reportcount"></span></a> ';
}
My second issue is in another JavaScript that I'm using to try to make these HTML pages function dynamically. I'm using AJAX to interact with a PHP script, however, when it brings back the page (basically taking the next page and straps it next to the first one with CSS, creating a 'duo view'), my JavaScript doesn't get applied, like time settings, or generated links. How does one deal with that? I can't use window.onload as it can only be used once - it's being used in another JavaScript. Do I just somehow modify the results of the PHP file as they come? I'm using a Regular Expression to grab the next page, so I can't actually modify the results inside the PHP... can I?
Willing to concede to most solutions, including rewrites.
Answer to the First Question
This is a little JavaScript "problem" caused by the browser's Back-Forward cache! I've already answered a similar question, but for completeness, here's the solution:
Firefox and the iOS Safari are the only ones (as of this writing) known to have this issue. The solution is to hook into window.unload event, and a specific condition to reload the page inside window.onpageshow!
Firefox fix
jQuery:
$(window).unload(function () { $(window).unbind('unload'); });
JavaScript:
function UnloadHandler() { window.removeEventListener('unload', UnloadHandler, false); }
window.addEventListener('unload', UnloadHandler, false);
iOS Safari fix
jQuery:
$(window).bind('pageshow', function(event) {
if (event.originalEvent.persisted) {
window.location.reload()
}
});
JavaScript:
window.addEventListener('pageshow', function (event) {
if (event.persisted) {
window.location.reload()
}
}, false);
Answer to the Second Question
You can actually hook into the onload event with multiple scripts/multiple functions. Instead of using the overwriting form of hooking into an event, such as ajaxtwo.onreadystatechange - you should "add" an event listener.
Simple example would be:
ajaxtwo.addEventListener('readystatechange', function () {
alert("hi! I'm done!");
}, false);
As for a helper function that will "do this for you" with backwards compatibility with some older versions of Internet Explorer; you can use this simple function:
function AttachEventListener(element, event, handler) {
if (element.addEventListener) {
element.addEventListener(event, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + event, handler);
} else {
alert('Invalid element specified for AttachEventListener');
}
}
Usage:
AttachEventListener(ajaxtwo, 'readystatechange', function () {
alert("hi! I'm using an awesome helper function!");
});
Related
My client is using an old classipress version, here's a github repo I found but what he's using is much older. Running the latest Wordpress version. It comes with plupload, some old version, couldn't find the version in the theme. Here's Functions.php, here's plupload. Here's the html of my page, no need to see it, but i'm putting it there because the page is protected so that's the only way to inspect the whole code if you want to.
I want to add the ability to upload multiple pictures at the same time, to do that, I add this to functions.php
add_filter('appthemes_plupload_config', 'enable_plupload_multisel', 10 ,1);
function enable_plupload_multisel($app_plupload_config){
$app_plupload_config['plupload']['multi_selection'] = true;
return $app_plupload_config; }
But I don't know how to stop the user from uploading more than 8 pictures? I tried adding max_files and max_files_count and max_file_count and nothing worked, I even modified the source code of the plugin itself and the js and nothing worked. I want to stop the user from being able to upload more than 8 images.
After I gave up on plupload, I tried doing it using Jquery, again didn't work
/* prevent form submission if user selects more than 8 pics */
jQuery('#app-attachment-upload-pickfiles').change(function() {
if (this.files.length > 8) {
alert('Uploading more than 8 images is not allowed');
this.value = '';
}
});
// Prevent submission if limit is exceeded.
jQuery('#mainform').submit(function() {
if (this.files.length > 8) {
jQuery('#app-attachment-upload-pickfiles').hide();
jQuery('#step1').hide();
return false;
} else {
jQuery('#app-attachment-upload-pickfiles').show();
jQuery('#step1').show();
}
});
Edit
My pluploadjs here. FilesAdded
attachUploader.bind('FilesAdded', function(up, files) {
jQuery.each(files, function(i, file) {
jQuery('#app-attachment-upload-filelist').append(
'<div id="' + file.id + '" class="app-attachment-upload-progress">' +
file.name + ' (' + plupload.formatSize(file.size) + ') <b></b>' +
'</div>');
window.appFileCount += 1;
APP_Attachment.hideUploadBtn();
});
up.refresh();
attachUploader.start();
});
I modified it to look like so
attachUploader.bind('FilesAdded', function(up, files) {
var maxfiles = 8;
if(up.files.length > maxfiles )
{
up.splice(maxfiles);
alert('no more than '+maxfiles + ' file(s)');
}
if (up.files.length === maxfiles) {
$('#app-attachment-upload-filelist').hide("slow"); // provided there is only one #uploader_browse on page
}
jQuery.each(files, function(i, file) {
jQuery('#app-attachment-upload-filelist').append(
'<div id="' + file.id + '" class="app-attachment-upload-progress">' +
file.name + ' (' + plupload.formatSize(file.size) + ') <b></b>' +
'</div>');
window.appFileCount += 1;
APP_Attachment.hideUploadBtn();
});
up.refresh();
attachUploader.start();
});
Is that all? Will it work now? I haven't tested it because it will give errors
I'm not sure but your code should almost work. I think you should manually remove the files from the queue by calling the removeFile method.
Maybe try this code:
attachUploader.bind('FilesAdded', function(up, files) {
var maxfiles = 8;
// remove all new files after the max of files
jQuery.each(up.files, function(i, file) {
if(i > maxfiles){
up.removeFile(file);
}
});
});
In this function, the value parameter is being passed down to fill in my URL. This works perfectly.
function showResults(results) {
var html = '';
$.each(results, function(index,value) {
html += '<li><img src="' + value.snippet.thumbnails.medium.url + '">' + value.snippet.title + '(More from ' + value.snippet.channelTitle + ')</li>';
});
$('#results').html(html);
}
In this nearly identical function, the value param loses its value. I can't see how. It's difficult to debug why this is happening because console.log() just returns "ReferenceError: $ is not defined" no matter what I check (it returns this in the first section too, which works well).
function showResults(results) {
var html = '';
$.each(results, function(index,value) {
html += '<li><img src="' + value.snippet.thumbnails.medium.url + '">' + value.snippet.title + ') </li>';
});
$('#results').html(html);
$('#results li a').click(function(){
playVid($(this).attr(value.id.videoId));
});
}
function playVid(vidID) {
var embedVid = 'https://www.youtube.com/embed/'+vidID+'?autoplay=1';
document.getElementById('player').src = embedVid;
}
Here I'm trying to push the value param (in the url again) to an iframe with id="player". The iframe receives an invalid param and the video won't play. Meanwhile the video plays in the first example. Where does value get lost?
value only exists within the scope of the each loop. So, first fix your reference error, and then I suggest the following changes in that second example:
1) Update the href with the videoId value in the each loop like in the first example:
<a href="https://www.youtube.com/watch?v=' + value.id.videoId + '">
2) And then launch the player with that value:
$('#results li a').click(function(e) {
e.preventDefault();
playVid($(this).attr('href'));
});
I'm using AJAX to add more articles to a list of articles when you press a button. So my AJAX call returns data that includes a title, author and 1 to 3 images associated with the article. Below is the code I'm using to output it, but it feels VERY clunky.
What are the best practices for printing out HTML with JavaScript/jQuery in a scenario like this where I need to add many new tags with new information? Thanks for the help!
Also, I know some of the code isn't super well written because it's a first draft just to make stuff work, so please only answer this question with regards to printing out the HTML or things that will make printing the HTML easier
$j.getJSON(ajaxurl, {action: 'load_articles', issues: $issues}, function(data) {
if (data.message != null) {
alert(data.message);
return
}
list = $j('.all-articles ul');
for (i in data.articles) {
article = data.articles[i];
//Hides articles already on page
if ($j("#" + article.id).size() === 0) {
list.append('<li class="article-preview" id="' + article.id + '">' +
'<h3 class="article-headline">' + article.title + '</h3>' +
'</li>');
current = $j("#" + article.id)
current.append('<p class="authors"></p>');
authors = $j("#" + article.id + " .authors")
for (a in article.authors) {
authors.append(article.authors[a].data.display_name + " ");
}
current.append('<div class="images"></div>');
images = $j("#" + article.id + " .images")
for (i in article.image) {
text = "<div class='image-expand-container'>";
if (i == 0) {
text += ('<img id="' + article.image[i].id + '"class="selected" src="' + article.image[i].medium + '"></img>');
}
else {
text += ('<img id="' + article.image[i].id + '" src="' + article.image[i].medium + '"></img>');
}
text += '<div class="dashicons dashicons-editor-expand"></div></div>';
images.append(text);
}
}
}
There are a few approaches you can take.
As you're doing here, you can return data from your ajax call (e.g. as JSON) and then use a javascript function to generate the corresponding HTML by building strings. This, as you're finding, is often messy.
You can generate the actual HTML on the server side, and have the ajax call return an HTML fragment, which you insert into your DOM. This has the advantage that, if some of your HTML is loading when the page loads, and some is loading via ajax, you can use the same approach (PHP, XSLT, ASP.NET Razor, any kind of server-side templating) to generate all of the HTML.
You can use a javascript templating framework to turn your JSON data into HTML. If all of your HTML is being generated via javascript (e.g. in a single-page application) this may be your best bet.
I've got a web app which works perfectly in the browser - both on the desktop and mobile.
The problem comes when I try to pretty it up by adding:
<meta name="apple-mobile-web-app-capable" content="yes" />
This works great too - up to the point I need to delete a record in the app.
I'm also using this great gist I found - https://gist.github.com/1042167 to stop the app switching into mobile safari:
<script type="text/javascript">
(function(document,navigator,standalone) {
// prevents links from apps from oppening in mobile safari
// this javascript must be the first script in your <head>
if ((standalone in navigator) && navigator[standalone]) {
var curnode, location=document.location, stop=/^(a|html)$/i;
document.addEventListener('click', function(e) {
curnode=e.target;
while (!(stop).test(curnode.nodeName)) {
curnode=curnode.parentNode;
}
// Conditions to do this only on links to your own app
// if you want all links, use if('href' in curnode) instead.
if(
'href' in curnode && // is a link
(chref=curnode.href).replace(location.href,'').indexOf('#') && // is not an anchor
( !(/^[a-z\+\.\-]+:/i).test(chref) || // either does not have a proper scheme (relative links)
chref.indexOf(location.protocol+'//'+location.host)===0 ) // or is in the same protocol and domain
) {
e.preventDefault();
location.href = curnode.href;
}
},false);
}
})(document,window.navigator,'standalone');
</script>
I'm wondering if this could be altered so data-method="delete" will play nice? at the minute - when I click 'Delete' - the 'are you sure?' confirm box hangs for a second or two, before dumping me back on the same Show page, with no delete having occured...
So I'm going to assume from what you've written that you are using the (bundled) jquery-ujs to handle your delete links for you as that sounds like what you're doing from your mention of data-method
The way the ujs handlers work for delete links is different to what you might expect. Here's the relevant bit of the jquery-ujs source:
// Handles "data-method" on links such as:
// Delete
handleMethod: function(link) {
var href = rails.href(link),
method = link.data('method'),
target = link.attr('target'),
csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content'),
form = $('<form method="post" action="' + href + '"></form>'),
metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
if (csrf_param !== undefined && csrf_token !== undefined) {
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
}
if (target) { form.attr('target', target); }
form.hide().append(metadata_input).appendTo('body');
form.submit();
},
So you can see that's what actually happening is a form is being dynamically created (with the required csrf data) and then submitted.
Your delegated click handler method from the gist you link to isn't going to work in this situation, as the rails js does a return: false on click on links with a data-method attribute that prevents your handler from ever firing.
The simplest thing to do is probably to roll your own (ajax based) delete handler based on the ujs code. For brevity I'm using the excellent jQuery form plugin to handle the actual submission:
function handleDeleteLink(endpoint){
var confirmed = confirm("Are you sure you want to delete this record?"),
csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content'),
form = $('<form method="post" action="' + endpoint + '"></form>'),
metadata_input = '<input name="_method" value="delete" type="hidden" />',
deleteOptions = {
beforeSubmit: function(){
//handle ajax start
},
success: function(el){
//hand success
},
error: function(){
}
};
if (csrf_param !== undefined && csrf_token !== undefined) {
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
}
if (confirmed){
form.hide()
.append(metadata_input)
.appendTo('body')
.submit(function(e){
e.preventDefault();
$(this).ajaxSubmit(options);
})
.submit();
}
}
In your view just replace yourdata-method with a class of say delete-link and bind via event delegation (or use a different data attr if you like):
$('body').on('click', '.delete-link', function(e){
e.preventDefault();
handleDeleteLink(e.target.href);
}
The problem is location.href = curnode.href; changes requests into GET requests, although I've had inconsistent behavior with POST and PUT requests. So when you have some routes such as:
GET /photos index
POST /photos create
GET /photos/new new
GET /photos/:id/edit edit
GET /photos/:id show
PUT /photos/:id update
DELETE /photos/:id destroy
The update and destroy routes end up rerouted to show. My hacky (hopefully temporary) solution is to just make custom routes for those requests that are being rerouted. So you would add:
match "update" => "Photos#update", :as => :update_photo
match "destroy" => "Photos#destroy", :as => :destroy_photo
I know, I know, this is not Rails convention, but it should work.
I'm trying to get the last 50 tweets using a certain hash tag, on a mobile device using PhoneGap (0.9.6) and jQuery (1.6.1). Here's my code:
function getTweets(hash, numOfResults) {
var uri = "http://search.twitter.com/search.json?q=" + escape(hash) + "&callback=?&rpp=" + numOfResults;
console.log("uri: " + uri);
$.getJSON(uri, function(data) {
var items = [];
if(data.results.length > 0) {
console.log("got " + data.results.length + " results");
$.each(data.results, function(key, val) {
var item = "<li>";
item += "<img width='48px' height='48px' src='" + val.profile_image_url + "' />";
item += "<div class='tweet'><span class='author'>" + val.from_user + "</span>";
item += "<span class='tweettext'>" + val.text + "</span>";
item += "</div>";
item += "</li>";
items.push(item);
});
}
else {
console.log("no results found for " + hash);
items.push("<li>No Tweets about " + hash + " yet</li>");
}
$("#tweetresults").html($('<ul />', {html: items.join('')}));
});
}
This code works great in a browser, and for a while worked in the iPhone simulator. Now it's not working on either the iPhone or Android simulator. I do not see any of the console logs and it still works in a browser.
What am I doing wrong? If it's not possible to call getJson() on a mobile device using PhoneGap, what is my alternative (hopefully without resorting to native code - that would beat the purpose).
Bonus: how can I debug this on a mobile simulator? In a browser I use the dev tools or Firebug, but in the simulators, as mentioned, I don't even get the log messages.
As always, thanks for your time,
Guy
Update:
As #Greg intuited, the function wasn't called at all. Here's what I found and how I bypassed it:
I have this <a> element in the HTML Get tweets
Then I have this code in the $(document).ready() function:
$("#getTweets").click(function() {
var hash = "#bla";
getTweets(hash, 50);
});
That didn't call the function. But once I changed the code to:
function gt() {
var hash = "#bla";
getTweets(hash, 50);
}
and my HTML to:
Get Tweets
it now works and calls Twitter as intended. I have no idea what's screwed up with that particular click() binding, but I ran into similar issues with PhoneGap before. Any ideas are appreciated.
Considering that (a) there isn't much that could go wrong with the first line of your function and (b) the second line is a log command, then it would seem that the function isn't being called at all. You'll have to investigate the other code in your app.
Or are you saying that you don't have a way to read logged messages on your mobile devices?