I'm making a Blackjack game to exercise my Javascript skills. I had a bunch of alert() messages tied to the betting function to prevent invalid entries. In updating the code to have a more elegant message style than a browser alert, I wrote a function called alertModal() that pops up a message on the screen and then fades away. The message pops up the first time a user tries to enter an invalid bet, but does not pop up any other messages if the bet is invalid-- nothing happens. I know the placeBet() function is still running when the user clicks again, because if the bet is valid, dealFirstCards() runs and the game proceeds. So it seems to me that for some reason, the if/else portion of the placeBet() function is only running on the first click...
The game is live and running with this code at http://cnb-blackjack.netlify.com/game.html
Here is the javascript code in question:
// Player places a bet
$('div.bet').on('click', function() {
$(this).removeClass('glow');
$('.bet-button').addClass('glow');
});
$('.bet-button').on('click', function() {
event.preventDefault();
if (!placeBet.called) {
placeBet();
}
});
// PLACE BET
function placeBet() {
var $bet = parseInt($('.bet-input').val())
var $bank = parseInt($('.player-bank').text())
var $currentBet = $('.current-bet');
if (!isNaN($bet) && $bet <= $bank && $bet !== 0) {
$currentBet.text(' $' + $bet);
$('.bet input[type="text"]').val('');
$('.place-bet .hideaway').slideUp();
$('.player-bank').text($bank - $bet);
placeBet.called = true;
dealFirstCards();
} else if ($bet > $bank) {
var $message = 'Bet cannot exceed the amount in your Bank!';
alertModal($message);
} else if (isNaN($bet)) {
var $message = 'Enter a number, without "$".';
alertModal($message);
} else if ($bet === 0) {
var $message = "Betting nothing won't get you very far...";
alertModal($message);
}
}
// SHOW MODAL
function alertModal(message) {
$popUp = $('.alert-message');
$('.modal').removeClass('hide');
$popUp.text(message);
setTimeout(function() {
$('.modal').fadeOut(1000);
}, 1000);
}
function alertModal(message) {
$popUp = $('.alert-message');
$('.modal').show();
$popUp.text(message);
setTimeout(function() {
$('.modal').fadeOut(1000);
}, 1000);
}
As comments have explained, fadeOut is leaving the modal hidden after the first time it's clicked. Just call $(element).show(); on the modal to show it again and let fadeOut remove it.
So I'm trying to create a bookmarklet to make my tasks a little bit easier. I can click the button in an iframe, but I need to wait for the iframe to load to get what I need. Here's the last attempt at this:
(function()
{
var els = window.frames["mainFrame"].document.getElementsByName("pcp_PasswordInfoHandler:doShowpassword");
if(els.length == 0)
{
console.log("Button not found");
}
else
{
els[0].click();
window.frames["mainFrame"].document.addEventListener('DOMContentLoaded', function() {
var user = window.frames["mainFrame"].document.querySelector("#inp_mlabs_credential_login > div.field-value > div").textContent;;
var pass = window.frames["mainFrame"].document.querySelector("#inp_mlabs_portal_password_panel_key_password > div.field-value > div").textContent;;
console.log(user);
console.log(pass);
});
}
}());
I am making an experimental addon that will search for a single term on several different art sites at once.
The addon works by doing the following:
Opens a window asking for user input (the term to search for)
Stores this input
Starts a crude loop process
The loop opens the first tab in the list
The loop attaches runs the first script in the list
(these scripts pretty much all work the same way)
This script is attached as a worker to the tab
The script enters the term into a search box in the tab then submits it
The submit triggers the worker to be destroyed
Steps 4-8 repeat until the list runs out of tabs to open
Note: the number of these steps have nothing to do with the step numbers in the code
Main.js
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var journal_entry = require("sdk/panel").Panel({
contentURL: data.url("DeltaLogPanel.html"),
contentScriptFile: data.url("get-text.js")
});
// Creates the button
require("sdk/ui/button/action").ActionButton({
id: "make-post",
label: "Make post",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
var count = 0;
function handleClick(state) {
journal_entry.show();
}
journal_entry.once("show", function() {
journal_entry.port.emit("show");
});
function doit() {
console.log("Step 1, loop "+ count +" started!");;
if(count < sites.urls.length)
{
TabIt(count);
}
else
{
console.log("loop ended");
}
}
function TabIt(x) {
console.log("Step 2, tab "+ count +" is opening!");
tabs.open(sites.urls[count]);
handleTab(count);
}
//this is our cargo. There's normally more stuff in it
var Cargo = {
Title: ""
};
var sites = {
urls: ["https://www.sofurry.com/", "https://inkbunny.net/search.php", "http://www.furaffinity.net/search/", "https://www.weasyl.com/search", "http://www.deviantart.com/"],
scripts: ["searchSF.js", "searchIB.js", "searchFA.js", "searchWS.js", "searchDA.js"]
};
function handleTab(X)
{
console.log("Step 3, tab "+ count +" is processing!");
//I tried tabs.on('load'.. and that didn't fix the problem
tabs.on('ready', function RunPostScript(tab)
{
console.log("The tab is ready");
worker = tab.attach(
{
contentScriptFile: sites.scripts[X],
contentScriptOptions:
{
Cargo
}
});
worker.port.once("myMessage", function handleMyMessage()
{
console.log("Step 4, search "+ count +" is shuttin down!");
//tab.close();
worker.destroy();
});
tabs.removeListener("ready", RunPostScript);
console.log("Step 5, search "+ count +" should be finished!");
count=count+1;
console.log("Ready to do it again?");
doit();
});
}
journal_entry.port.once("cargo-shipping", function (cargo) {
Cargo.Title = cargo.title;
console.log("title: " + Cargo.Title);
//All data from panel should be imported. So panel is now hidden
journal_entry.hide();
//this starts the tab opening process
TabIt(count);
});
DeltaLogPanel.js
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- <link type="text/css" rel="stylesheet" href="DeltaLogPanel.css"/> -->
<style>
#MainPanel
{
width: 180px;
height:180px;
background-color: #ACA1A1;
}
</style>
</head>
<body id="MainPanel">
<div id="simpleOptions">
<textarea id="titleBox" placeholder="Title" rows="1"></textarea>
<button type="button" id="publishButton">Publish</button>
</div>
</body>
</html>
get-text.js
var titleArea = document.getElementById("titleBox");
var finalButton = document.getElementById("publishButton");
//this defines the cargo on the button press then ships it.
finalButton.addEventListener('click', function() {
// Remove the newline.
var cargo = {
title: ""
};
cargo.title = titleArea.value;
self.port.emit("cargo-shipping", cargo);
titleArea.value = "";
}, false);
//focusses on title box when button is pressed
self.port.on("show", function onShow() {
titleArea.focus();
});
searchFA.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var FAzA = document.querySelector("form#search-form fieldset input#q");
FAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-form").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchIB.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var IBzA = document.querySelector("#text");
IBzA.value = x;
};
//hits the submit button
var ShipIt = function(){
var x = document.querySelector("body > form:nth-child(12)");
var y =document.querySelector("body > form:nth-child(9)");
if (x !==null || y !==null)
{
if (x !== null)
{
x.submit();
}
else
{
y.submit();
}
}
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchSF.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var SFzA = document.querySelector("#headersearch");
SFzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector(".topbar-nav > form:nth-child(3)").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchWS.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var WSzA = document.querySelector("form#search-backup-search input");
WSzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-backup-search").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchDA.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var DAzA = document.querySelector("input.gmbutton2");
DAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("#search7").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
What does work
It stores the user input.
The addon loads all the tabs in the series perfectly.
The first script runs smoothly on the first tab.
When tested separately, each of the scripts runs the search result like they should.
The problem
When everything is finished, not all the tabs display the search results. Several of the search boxes are left blank as if the scripts never ran on those tabs.
The script of the first tab always works, but the scripts on the rest of the tabs have a random chance of not working.
I noticed the console.log("Step 4, search "+ count +" is shuttin down!"); is never executed. I think this means the worker is never destroyed.
That might be causing these issues.
The worker for each script should be destroyed after the script runs the following: self.port.emit("myMessage");
This little segment of code is at the top of every script. But I think the submit process happens too fast for the event listener to be triggered.
Error message
I am getting the following error message for each of the scripts that do not work:
Object
- _errorType = TypeError
- message = FAzA is null
- fileName = resource://gre/modules/commonjs/toolkit/loader.js -> resource:/
/gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at
-jetpack/my-addon/data/searchFA.js
- lineNumber = 8
- stack = titlePort#resource://gre/modules/commonjs/toolkit/loader.js -> res
ource://gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1- tbpzbqttco
eaag-at-jetpack/my- addon/data/searchFA.js:8:2|Finalize#resource://gre/modules/co
mmonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/loader/sandbox.j
s -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my- addon/data/searchFA.js:15:1|#r
esource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commo
njs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my- addon/
data/searchFA.js:18:1|
- name = TypeError
If only one tab/script is run, this error never happens. This is true for every tab/script combination. So I know they should all work.
My Question to you
How can I destroy the worker of each script after the script runs the submit function?
Your error:
FAzA is null
indicates that the element was not found in the page when using querySelector().
The problem is mostly here:
tabs.on('ready', function RunPostScript(tab) {...});
This monitors all the tabs and creates a race condition when submitting a search. The following happens:
attach ready event listener to tabs waiting for site A
open site A
page A fires ready event, attach script A to site A
remove ready event listener
increment count
end of loop, call next tab
attach ready event listener to tabs waiting for site B
meanwhile, site A submits the search, it loads the result page and fires a ready event
attach script B to site A
site A is now processed as site B, script B can't find the search input, outputs error
remove ready event listener
increment count
end of loop, call next tab
attach ready event listener to tabs waiting for site C
etc
You should monitor events at the tab level. Use once() so you don't have to remove the listener. Something like this:
function TabIt(x) {
tabs.open({
url:sites.urls[x],
onOpen: function(x, tab) {
// fire once
// count is available in this scope, no need to setup as arg again
tab.once('ready', function readyStuff(tab) {
tab.attach({
contentScriptFile: sites.scripts[x],
contentScriptOptions:{Cargo}
});
});
}
});
++count;
doit();
}
You also keep using count instead of x, while it's possible to do so, it would require binding the value because of the scope in which count exists. But since you have x, just use it.
I also advised in comments to use DOMContentLoaded in attached scripts. After reading more on script attachment to tabs, that was bad advice.
Your code could be vastly optimized but let's do one thing at a time.
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var aDOMWindow = getMostRecentBrowserWindow();
if (aDOMWindow.gBrowser && aDOMWindow.gBrowser.tabContainer) {
var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
for (var i=0; i<tabs.length; i++) {
// tabs[i].contentWindow.wrappedJSObject.... // not e10s friendly, so to tabs[i].messageManager or something like that
}
}
here is frame scripts which are absolutely easy:
https://github.com/mdn/e10s-example-addons/tree/master/run-script-in-all-pages
I'm using this jQuery script to show search results. Everything works fine, but when search results have more than one page and I'm browsing pages via paging then every page loading is gradually getting slower. Usually first cca 10 pages loads I get quickly, but next are getting avoiding loading delay. Whole website get frozen for a little while (also loader image), but browser is not yet. What should be the problem?
function editResults(def) {
$('.searchResults').html('<p class=\'loader\'><img src=\'images/loader.gif\' /></p>');
var url = def;
var url = url + "&categories=";
// Parse Categories
$('input[name=chCat[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
url = url + "&sizes=";
// Parse Sizes
$('input[name=chSize[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
url = url + "&prices=";
// Parse Prices
$('input[name=chPrice[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
$('.searchResults').load('results.php'+url);
$('.pageLinks').live("click", function() {
var page = this.title;
editResults("?page="+page);
});
}
$(document).ready(function(){
editResults("?page=1");
// Check All Categories
$('input[name=chCat[0]]').click(function() {
check_status = $('input[name=chCat[0]]').attr("checked");
$('input[name=chCat[]]').each(function() {
this.checked = check_status;
});
});
// Check All Sizes
$('input[name=chSize[0]]').click(function() {
check_status = $('input[name=chSize[0]]').attr("checked");
$('input[name=chSize[]]').each(function() {
this.checked = check_status;
});
});
// Edit Results
$('.checkbox').change(function() {
editResults("?page=1");
});
// Change Type
$(".sort").change(function() {
editResults("?page=1&sort="+$(this).val());
});
});
$('.pageLinks').live("click", function() {
var page = this.title;
editResults("?page="+page);
});
just a wild guess but... wouldn't this piece of code add a new event handler to the click event instead reaplacing the old one with a new one? causing the click to call all the once registered handlers.
you should make the event binding just once
var global_var = '1';
function editResults(def) {
// all your code
global_var = 2; // what ever page goes next
};
$(document).ready(function() {
// all your code ...
$('.pageLinks').live("click", function() {
var page = global_var;
editResults("?page="+page);
});
});
I am using this script from: http://pop.seaofclouds.com/
The problem is if you call the script multiple times it causes a cascading effect of a pop-out within a pop-out for as many times as you call the script.
I'm trying to figure out how to prevent it from executing when the popout has already been set. Here's the script:
//
// pop! for jQuery
// v0.2 requires jQuery v1.2 or later
//
// Licensed under the MIT:
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright 2007,2008 SEAOFCLOUDS [http://seaofclouds.com]
//
(function($) {
$.pop = function(options){
// inject html wrapper
function initpops (){
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
}
initpops();
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });
$("body").on("click", ".pop", function(){
closeInactivePop();
});
// toggle that pop
$("body").on("click", ".pop_toggle", function(){
$(this).parent(".pop").toggleClass("active");
});
}
})(jQuery);
now when i load this script on an ajax call the new pop-out menus work but the old ones do not react to the onclick event.
You shouldn't mess with the plugin. It works exactly like it should.
Better show us how you call this on elements that you already have.
Also I don't like this plugin. Better use something from JqueryUI
You can do such thing in much easier way.
[edit]
I tried your first code (the plugin) and it works correctly for me.
[edit]
OK. I get it. You call $.pop(); multiple times. You shouldn't! Calling $.pop(); will pin up the drop down menu to all elements that has class="pop". This is the reason why you have such funny stack.
Just use $.pop(); once.
Plugin doesn't give ability to connect NEW elements that was dynamically created on the page.
Removed pop from ajax call and just called this on success:
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });