I found myself reading the same questions over and over, so I wanted a way to hide questions.
I have a script to does what is suppose to do, however it cripples existing javascript, such as the upvote button and adding tags when asking questions. Does anyone know why this is happening, or how to fix it?
Edit: oh, in the error console I am getting:
Error: $ is not a function
Source File: http://cdn.sstatic.net/js/stub.js?v=b7084478a9a4
Line: 1
Edit2:
The solution
(fixed #17/06/2014)
// ==UserScript==
// #name StackOverflowHidePosts
// #namespace StackOverflowHidePosts
// #description Allows you to hide questions on Stack Overflow.
// #include http://stackoverflow.com/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
// ==/UserScript==
var idListString = GM_getValue('idList', '');
var idList = idListString.split(',');
GM_setValue('idList', idList.join(','));
function getId (idString)
{
return idString.split('-')[2];
}
function removeQuestion (e)
{
var id = getId(e.data.questionSummaryDiv.id);
$(e.data.questionSummaryDiv).hide(250);
idList.push(id);
setTimeout(function() {
GM_setValue('idList', idList.join(','));
}, 0);
return false;
}
$('div.question-summary').each(function (index, questionSummaryDiv)
{
var id = getId(questionSummaryDiv.id);
if (idList.indexOf(id) != -1)
{
$(questionSummaryDiv).hide();
return;
}
var link = $('<a><em>(Hide Post)</em></a>');
link.attr('href', '#' + questionSummaryDiv.id);
link.click({questionSummaryDiv: questionSummaryDiv}, removeQuestion);
$('div.started', questionSummaryDiv).append(link);
});
Never inject JS if you don't have to, and never use the page's jQuery in FF GM -- that's the main source of errors in this case.
The entire script should be:
// ==UserScript==
// #name StackOverflowImTooStupidMarker
// #namespace StackOverflowImTooStupidMarker
// #description Allows you to hide questions on Stack Overflow when you can't answer them.
// #include http://stackoverflow.com/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// ==/UserScript==
var idListString = GM_getValue('idList', '');
var idList = idListString.split(',');
GM_setValue('idList', idList.join(','));
function getId (idString)
{
return idString.split('-')[2];
}
function removeQuestion (e)
{
var id = getId(e.data.questionSummaryDiv.id);
$(e.data.questionSummaryDiv).hide(250);
idList.push(id);
setTimeout(function() {
GM_setValue('idList', idList.join(','));
}, 0);
return false;
}
$('div.question-summary').each(function (index, questionSummaryDiv)
{
var id = getId(questionSummaryDiv.id);
if (idList.indexOf(id) != -1)
{
$(questionSummaryDiv).hide();
return;
}
var link = $('<a><em>(Too Stupid)</em></a>');
link.attr('href', '#' + questionSummaryDiv.id);
link.click({questionSummaryDiv: questionSummaryDiv}, removeQuestion);
$('div.started', questionSummaryDiv).append(link);
});
That script attempts to include jQuery first thing:
(function()
{
if (typeof unsafeWindow.jQuery == 'undefined')
{
var GM_Head = document.getElementsByTagName('head')[0] || document.documentElement, GM_JQ = document.createElement('script');
GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
GM_JQ.type = 'text/javascript';
GM_JQ.async = true;
GM_Head.insertBefore(GM_JQ, GM_Head.firstChild);
}
GM_wait();
})();
This issue there is that jQuery is guaranteed to be loaded on Stack Overflow to begin with...if it's not present you have much bigger issues. That whole jQuery replacement shouldn't happen, as it's both impacting already registered plugins (nuking then) and using a newer version of jQuery that Stack Exchange currently does, meaning other potentially breaking changes as well.
Since the script needs none of the latest functionality, that entire chunk above should simply be:
GM_wait();
For the other issues, there are a few more $ conflicts...but you still want to be safe with respect to load order here. Here's a cheaper and still safe version that...well, works:
var idListString = GM_getValue('idList', '');
var idList = idListString.split(',');
GM_setValue('idList', idList.join(','));
GM_wait();
function GM_wait() {
if (typeof unsafeWindow.jQuery == 'undefined') {
window.setTimeout(GM_wait, 100);
return;
}
unsafeWindow.jQuery(function($) {
var link = $('<em>(Too Stupid)</em>').click(removeQuestion);
$('div.question-summary').each(function (index, questionSummaryDiv) {
var id = getId(questionSummaryDiv.id);
if (idList.indexOf(id) != -1) {
$(questionSummaryDiv).hide();
} else {
$('div.started', questionSummaryDiv).append(link.clone(true));
}
});
});
}
function getId (idString) {
return idString.split('-')[2];
}
function removeQuestion () {
var q = unsafeWindow.jQuery(this).closest("div.question-summary").hide(250);
idList.push(getId(q.attr("id")));
setTimeout(function() {
GM_setValue('idList', idList.join(','));
}, 0);
return false;
}
Related
I'm using the Microsoft Translation Widget, which I'd like to use to automatically translate a webpage without user interaction.
The problem is, I can't get rid of the widget that keeps popping up or hide it on document.ready because the CSS and JS get loaded from Microsoft's own script in the widget!
Does anyone know a way around this? I've looked everywhere and cannot find a solutuion for this.
Whoa, after some time playing around with that, I've finally achieved what you want.
It's kindda ugly, because of some needed workarounds, but it works, take a look at the fiddle.
The steps were:
Firstly, we must override the default addEventListener behavior:
var addEvent = EventTarget.prototype.addEventListener;
var events = [];
EventTarget.prototype.addEventListener = function(type, listener) {
addEvent.apply(this, [].slice.call(arguments));
events.push({
element: this,
type: type,
listener: listener
});
}
Then, we create a helper function removeEvents. It removes all the event listeners of an element.
var removeEvents = function(el, type) {
var elEvents = events.filter(function(ev) {
return ev.element === el && (type ? ev.type === type : true);
});
for (var i = 0; i < elEvents.length; i++) {
el.removeEventListener(elEvents[i].type, elEvents[i].listener);
}
}
When creating the script tag, in the way Microsoft says:
var s = d.createElement('script');
s.type = 'text/javascript';
s.charset = 'UTF-8';
s.src = ((location && location.href && location.href.indexOf('https') == 0) ? 'https://ssl.microsofttranslator.com' : 'http://www.microsofttranslator.com') + '/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**&ctf=True&ui=true&settings=Manual&from=';
var p = d.getElementsByTagName('head')[0] || d.dElement;
p.insertBefore(s, p.firstChild);
We must add a load event listener to that script, and the code below is fully commented:
s.addEventListener('load', function() {
// when someone changes the translation, the plugin calls the method TranslateArray
// then, we save the original method in a variable, and we override it
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
// we call the original method
translate.apply(this, [].slice.call(arguments));
// since the translation is not immediately available
// and we don't have control when it will be
// I've created a helper function to wait for it
waitForTranslation(function() {
// as soon as it is available
// we get all the elements with an attribute lang
[].forEach.call(d.querySelectorAll('[lang]'), function(item, i) {
// and we remove all the mouseover event listeners of them
removeEvents(item, 'mouseover');
});
});
}
// this is the helper function which waits for the translation
function waitForTranslation(cb) {
// since we don't have control over the translation callback
// the workaround was to see if the Translating label is visible
// we keep calling the function, until it's hidden again
// and then we call our callback
var visible = d.getElementById('FloaterProgressBar').style.visibility;
if (visible === 'visible') {
setTimeout(function() {
waitForTranslation(cb);
}, 0);
return;
}
cb();
}
});
Update 1
After re-reading your question, it seems you want to hide all the widgets at all.
So, you must add the following code as soon as the translation is got:
waitForTranslation(function() {
document.getElementById('MicrosoftTranslatorWidget').style.display = 'none';
document.getElementById('WidgetLauncher').style.display = 'none';
document.getElementById('LauncherTranslatePhrase').style.display = 'none';
document.getElementById('TranslateSpan').style.display = 'none';
document.getElementById('LauncherLogo').style.display = 'none';
document.getElementById('WidgetFloaterPanels').style.display = 'none';
// rest of the code
});
I've created another fiddle for you, showing that new behavior.
Update 2
You can prevent the widget showing at all by adding the following CSS code:
#MicrosoftTranslatorWidget, #WidgetLauncher, #LauncherTranslatePhrase, #TranslateSpan, #LauncherLogo, #WidgetFloaterPanels {
opacity: 0!important;
}
And you can even prevent the before-translated text being showed, by hiding the document.body by default, and then showing it when the page is fully translated:
(function(w, d) {
document.body.style.display = 'none';
/* (...) */
s.addEventListener('load', function() {
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
translate.apply(this, [].slice.call(arguments));
waitForTranslation(function() {
/* (...) */
document.body.style.display = 'block';
});
}
});
});
Take a look at the final fiddle I've created.
For me, this was the solution:
on your < style > section add this class
.LTRStyle { display: none !important }
Also, if you are invoking the translation widget this way:
Microsoft.Translator.Widget.Translate('en', lang, null, null, TranslationDone, null, 3000);
then add this to your callback (in this example is TranslationDone) function:
function TranslationDone() {
Microsoft.Translator.Widget.domTranslator.showHighlight = false;
Microsoft.Translator.Widget.domTranslator.showTooltips = false;
document.getElementById('WidgetFloaterPanels').style.display = 'none';
};
I am using the following excellent userscript (for Chrome, Chrome's Tampermonkey and Firefox Greasemonkey). It's supposed to display movie ratings next to each IMDb movie link, but it stopped working properly.
This is the complete script:
// ==UserScript==
// #name Add IMDb rating next to all IMDb links (+voter count)
// #author Ali
// #description Adds movie ratings and number of voters to any IMDb link. Modified version of http://userscripts.org/scripts/show/9174
// #include *
// #version 2013-05-12
// #namespace http://userscripts.org/scripts/show/96884
// #grant GM_xmlhttpRequest
// #downloadURL http://www.alibakir.com/upload/addimdbratings.js
// #updateURL http://www.alibakir.com/upload/addimdbratings.js
// ==/UserScript==
var IMDBpluginlinks = document.links;
var IMDBcontinueelement=document.createElement("button");
IMDBcontinueelement.innerHTML="Get rating";
function processIMDBLinks(s){
IMDBcontinueelement.style.display = 'none';
var r=0;
for (IMDBi = s; IMDBi < IMDBpluginlinks.length; IMDBi++) {
if (IMDBpluginlinks[IMDBi].href.indexOf("/title/") != -1 && IMDBpluginlinks[IMDBi].href.indexOf("imdb.") != -1){
if(r>300){
IMDBcontinueelement.onclick=function(){ processIMDBLinks(IMDBi); };
IMDBcontinueelement.style.display='inline';
IMDBpluginlinks[IMDBi].parentNode.insertBefore(IMDBcontinueelement, IMDBpluginlinks[IMDBi]);
break;
}
r++;
GM_xmlhttpRequest({
method: 'get',
headers: {
},
url: IMDBpluginlinks[IMDBi].href,
onload: function (IMDBi){return function(result) {
rating = result.responseText.match(/Users rated this (.*) \(/);
votes = result.responseText.match(/\((.*) votes\) -/);
IMDBpluginlinks[IMDBi].parentNode.insertBefore(document.createElement("span"), IMDBpluginlinks[IMDBi]).innerHTML = (rating ? "<b> [" + rating[1] + " - "+votes[1]+"] </b>" : "<b style='color: red;'>[NA] </b> ");
}}(IMDBi)
});
}
}
}
processIMDBLinks(0);
And this is how an example page looks at the moment:
As you can see, the script displays the results in the wrong places.
Why does it present them in the wrong place and how can it be fixed?
The issue causing your main complaint (results in the wrong places) is that GM_xmlhttpRequest operates asynchronously (which is good) and the onload was improperly constructed.
You either need to wrap the call to GM_xmlhttpRequest in a proper closure or provide a context in the GM_xmlhttpRequest call. (See the code below.)
For more information on why closures are needed, see this answer to the same type of problem.
Other big problems include AJAX-fetching dozens of improper links and firing on every page and iframe. Both of these slow the browser down quite a bit.
Don't use #include *. Even if you don't mind, other users of the script will. Add #include or #match lines for just the sites that you know have IMDB links.
I thought that I might want to use this script myself, so I started cleaning it up. You can read the inline comments and compare to the original script for an idea of some of the lesser problems. (Don't use onclick and always check match returns, etc.)
// ==UserScript==
// #name add IMDb rating next to all IMDb links (+voter count)
// #description Adds movie ratings and number of voters to any imdb link. Modified version of http://userscripts.org/scripts/show/96884
// #match *://www.imdb.com/*
// #grant GM_xmlhttpRequest
// ==/UserScript==
var maxLinksAtATime = 50; //-- pages can have 100's of links to fetch. Don't spam server or browser.
var fetchedLinkCnt = 0;
function processIMDB_Links () {
//--- Get only links that could be to IMBD movie/TV pages.
var linksToIMBD_Shows = document.querySelectorAll ("a[href*='/title/']");
for (var J = 0, L = linksToIMBD_Shows.length; J < L; J++) {
var currentLink = linksToIMBD_Shows[J];
/*--- Strict tests for the correct IMDB link to keep from spamming the page
with erroneous results.
*/
if ( ! /^(?:www\.)?IMDB\.com$/i.test (currentLink.hostname)
|| ! /^\/title\/tt\d+\/?$/i.test (currentLink.pathname)
)
continue;
if (! currentLink.getAttribute ("data-gm-fetched") ){
if (fetchedLinkCnt >= maxLinksAtATime){
//--- Position the "continue" button.
continueBttn.style.display = 'inline';
currentLink.parentNode.insertBefore (continueBttn, currentLink);
break;
}
fetchTargetLink (currentLink); //-- AJAX-in the ratings for a given link.
//---Mark the link with a data attribute, so we know it's been fetched.
currentLink.setAttribute ("data-gm-fetched", "true");
fetchedLinkCnt++;
}
}
}
function fetchTargetLink (linkNode) {
//--- This function provides a closure so that the callbacks can work correctly.
/*--- Must either call AJAX in a closure or pass a context.
But Tampermonkey does not implement context correctly!
(Tries to JSON serialize a DOM node.)
*/
GM_xmlhttpRequest ( {
method: 'get',
url: linkNode.href,
//context: linkNode,
onload: function (response) {
prependIMDB_Rating (response, linkNode);
},
onload: function (response) {
prependIMDB_Rating (response, linkNode);
},
onabort: function (response) {
prependIMDB_Rating (response, linkNode);
}
} );
}
function prependIMDB_Rating (resp, targetLink) {
var isError = true;
var ratingTxt = "** Unknown Error!";
if (resp.status != 200 && resp.status != 304) {
ratingTxt = '** ' + resp.status + ' Error!';
}
else {
if (/\(awaiting \d+ votes\)|\(voting begins after release\)|in development/i.test (resp.responseText) ) {
ratingTxt = "NR";
isError = false;
}
else {
var ratingM = resp.responseText.match (/Users rated this (.*) \(/);
var votesM = resp.responseText.match (/\((.*) votes\) -/);
if (ratingM && ratingM.length > 1 && votesM && votesM.length > 1) {
isError = false;
ratingTxt = ratingM[1] + " - " + votesM[1];
}
}
}
var resltSpan = document.createElement ("span");
resltSpan.innerHTML = '<b> [' + ratingTxt + '] </b> ';
if (isError)
resltSpan.style.color = 'red';
//var targetLink = resp.context;
//console.log ("targetLink: ", targetLink);
targetLink.parentNode.insertBefore (resltSpan, targetLink);
}
//--- Create the continue button
var continueBttn = document.createElement ("button");
continueBttn.innerHTML = "Get ratings";
continueBttn.addEventListener ("click", function (){
fetchedLinkCnt = 0;
continueBttn.style.display = 'none';
processIMDB_Links ();
},
false
);
processIMDB_Links ();
As per this post Log a web page's dynamically-created tag attributes with a userscript
i retrieved the attributes and also included the array as suggested like
addJS_Node ("var arr=[];");
i successfully pushed the src attributes to arr array because i didn't get any error.
Now i am confused where to display the array.
My code is as follows
// ==UserScript==
// #name Tags Monitoring
// #namespace PC
// #run-at document-start
// ==/UserScript==
//--- Intercept and log document.createElement().tagattributes
addJS_Node ("var arr=[];");
function LogNewTagCreations ()
{
var oldDocumentCreateElement = document.createElement;
document.createElement = function (tagName)
{
var elem = oldDocumentCreateElement.apply (document, arguments);
if(tagName=='script')
GetScriptAttributes (elem);
return elem;
}
}
function GetScriptAttributes (elem, tagNum, timerIntVar)
{
/*--- Because the tags won't be set for some while, we need
to poll for when they are added.
*/
GetScriptAttributes.tagNum = GetScriptAttributes.tagNum || 0;
if ( ! tagNum)
{
GetScriptAttributes.tagNum++;
tagNum = GetScriptAttributes.tagNum;
}
/*-- Getting the required attributes */
if (elem.src)
{
doneWaiting ();
arr.push(elem.src);
console.log (
tagNum," has a src attribute of:", elem.src,"=========",arr.length
);
}
else
{
if ( ! timerIntVar)
{
var timerIntVar = setInterval
(
function ()
{
GetScriptAttributes (elem, tagNum, timerIntVar);
},
50
);
}
}
function doneWaiting ()
{
if (timerIntVar)
{
clearInterval (timerIntVar);
}
}
}
/*--- The userscript or GM script will start running before the DOM is available.
Therefore, we wait...
*/
var waitForDomInterval = setInterval (
function () {
var domPresentNode;
if (typeof document.head == "undefined")
domPresentNode = document.querySelector ("head, body");
else
domPresentNode = document.head;
if (domPresentNode) {
clearInterval (waitForDomInterval);
addJS_Node (GetScriptAttributes.toString() );
addJS_Node (null, null, LogNewTagCreations);
}
},
1
);
addJS_Node("document.onreadystatechange = function (){if (document.readyState == 'complete'){console.log(arr.length);}}");
//--- Handy injection function.
function addJS_Node (text, s_URL, funcToRun) {
var D = document;
var scriptNode = D.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
I want to print the number of tags created dynamically that is why i added array. I first tried with script tags only and how can i know the array is completed and so that i can print the array length
Looks like your problem is not with your code.
You simply dont know javascript at all.
I strongly recommend you start with simple codes and tutorials.
Try to code your first script.
If you have any doubt about your code, feel free to come and ask about it.
The code that Brock Adams gave you is working perfectly.
I just copy-pasted it into Greasemonkey.
Here is the output:
And please go back to Log a web page's dynamically-created tag attributes with a userscript and mark his answer as the right one.
I'm making a Greasemonkey script to add download links beside videos on cnn.com.
I used a saved version of the HTML page to test my script and was able to get it to work perfectly. Then when I put the javascript into Greasemonkey and tried it on the actual site, it didn't work.
This is the not the full script, but the part of the script with the problem. It is simply supposed to add a link at the bottom of every div with the "sec_video_box" class (as seen in the picture).
// ==UserScript==
// #name CNN Download
// #namespace Cool
// #description CNN Download
// #include http://*cnn.com/video/*
// ==/UserScript==
var getClass = function(clssName, rootNode /*optional*/){
var root = rootNode || document,
clssEls = [],
elems,
clssReg = new RegExp("\\b"+clssName+"\\b");
// use the built in getElementsByClassName if available
if (document.getElementsByClassName){
return root.getElementsByClassName(clssName);
}
// otherwise loop through all(*) nodes and add matches to clssEls
elems = root.getElementsByTagName('*');
for (var i = 0, len = elems.length; i < len; i+=1){
if (clssReg.test(elems[i].className)) clssEls.push(elems[i])
}
return clssEls;
};
function insertlinks() {
var boxes = getClass("sec_video_box");
for (i=0; i<boxes.length; i++) {
var theboxid = boxes[i].getAttribute("id");
document.getElementById(theboxid).innerHTML = document.getElementById(theboxid).innerHTML + 'link';
}
}
window.onload = insertlinks ();
Can someone tell me what I'm doing wrong?
The 3 biggest problems with that script are:
You can't use window.onload that way; see GM Pitfall #2: Event Handlers. Always use addEventListener() or jQuery.
Those video objects are AJAXed-in after the document loads, anyway.
Those video objects can change, via AJAX; so you'll want to monitor for new objects.
There are some minor issues but first note that the entire existing script can be simplified to this, with jQuery:
// ==UserScript==
// #name CNN Download
// #namespace Cool
// #description CNN Download
// #include http://*cnn.com/video/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// ==/UserScript==
function insertlinks () {
var boxes = $(".sec_video_box");
boxes.each ( function () {
var theboxid = this.id;
$(this).append ('link');
} );
}
$(window).load (insertlinks);
(Important: This sample code will still not work.)
Handling the AJAX issues, it becomes:
// ==UserScript==
// #name CNN Download
// #namespace Cool
// #description CNN Download
// #include http://*cnn.com/video/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// ==/UserScript==
function insertlink (jNode) {
var theboxid = jNode.attr ('id');
jNode.append ('link');
}
waitForKeyElements (".sec_video_box", insertlink, false);
function waitForKeyElements (
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
)
{
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents ()
.find (selectorTxt);
if (targetNodes && targetNodes.length > 0) {
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each ( function () {
var jThis = $(this);
var alreadyFound = jThis.data ('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
actionFunction (jThis);
jThis.data ('alreadyFound', true);
}
} );
btargetsFound = true;
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace (/[^\w]/g, "_");
var timeControl = controlObj [controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval (timeControl);
delete controlObj [controlKey]
}
else {
//--- Set a timer, if needed.
if ( ! timeControl) {
timeControl = setInterval ( function () {
waitForKeyElements ( selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
500
);
controlObj [controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}
(Which does work.)
window.onload = insertlinks;
Remove the (), they cause the function to run immediately and the return value (null in this case) is assigned to onload. Locally, this is fine since loading is near-instant. Online, however, it will fail.
I'm trying to build a Javascript listener for a small page that uses AJAX to load content based on the anchor in the URL. Looking online, I found and modified a script that uses setInterval() to do this and so far it works fine. However, I have other jQuery elements in the $(document).ready() for special effects for the menus and content. If I use setInterval() no other jQuery effects work. I finagled a way to get it work by including the jQuery effects in the loop for setInterval() like so:
$(document).ready(function() {
var pageScripts = function() {
pageEffects();
pageURL();
}
window.setInterval(pageScripts, 500);
});
var currentAnchor = null;
function pageEffects() {
// Popup Menus
$(".bannerMenu").hover(function() {
$(this).find("ul.bannerSubmenu").slideDown(300).show;
}, function() {
$(this).find("ul.bannerSubmenu").slideUp(400);
});
$(".panel").hover(function() {
$(this).find(".panelContent").fadeIn(200);
}, function() {
$(this).find(".panelContent").fadeOut(300);
});
// REL Links Control
$("a[rel='_blank']").click(function() {
this.target = "_blank";
});
$("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
$("#content").fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
}
}
This works fine for a while but after a few minutes of the page being loaded, it drags to a near stop in IE and Firefox. I checked the FF Error Console and it comes back with an error "Too many Recursions." Chrome seems to not care and the page continues to run more or less normally despite the amount of time it's been open.
It would seem to me that the pageEffects() call is causing the issue with the recursion, however, any attempts to move it out of the loop breaks them and they cease to work as soon as setInterval makes it first loop.
Any help on this would be greatly appreciated!
I am guessing that the pageEffects need added to the pageURL content.
At the very least this should be more efficient and prevent duplicate handlers
$(document).ready(function() {
pageEffects($('body'));
(function(){
pageURL();
window.setTimeout(arguments.callee, 500);
})();
});
var currentAnchor = null;
function pageEffects(parent) {
// Popup Menus
parent.find(".bannerMenu").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
subMenu: $(this).find("ul.bannerSubmenu"),
handlerIn: function() {
this.subMenu.slideDown(300).show();
},
handlerOut: function() {
this.subMenu.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
parent.find(".panel").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
content: panel.find(".panelContent"),
handlerIn: function() {
this.content.fadeIn(200).show();
},
handlerOut: function() {
this.content.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
// REL Links Control
parent.find("a[rel='_blank']").each(function() {
$(this).target = "_blank";
});
parent.find("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
var content = $("#content");
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
content.fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
pageEffects(content);
}
}
Thanks for the suggestions. I tried a few of them and they still did not lead to the desirable effects. After some cautious testing, I found out what was happening. With jQuery (and presumably Javascript as a whole), whenever an AJAX callback is made, the elements brought in through the callback are not binded to what was originally binded in the document, they must be rebinded. You can either do this by recalling all the jQuery events on a successful callback or by using the .live() event in jQuery's library. I opted for .live() and it works like a charm now and no more recursive errors :D.
$(document).ready(function() {
// Popup Menus
$(".bannerMenu").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find("ul.bannerSubmenu").slideDown(300);
} else {
$(this).find("ul.bannerSubmenu").slideUp(400);
}
});
// Rollover Content
$(".panel").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find(".panelContent").fadeIn(200);
} else {
$(this).find(".panelContent").fadeOut(300);
}
});
// HREF Events
$("a[rel='_blank']").live("click", function(event) {
var target = $(this).attr("href");
window.open(target, "_blank");
event.preventDefault();
});
$("a[rel='share']").live("click", function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
setInterval("checkAnchor()", 500);
});
var currentAnchor = null;
function checkAnchor() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn(200);
$("#content").fadeOut(200).html(data).fadeIn(200);
$("#load").fadeOut(200);
});
}
}
Anywho, the page works as intended even in IE (which I rarely check for compatibility). Hopefully, some other newb will learn from my mistakes :p.