Force line to execute before closure - javascript

I have the following code
(function() {
var weather = new Weather();
var input = document.getElementById("inputCity");
var weatherHolder = document.getElementsByClassName("weather");
var loading = document.getElementById("loadingSign");
input.focus();
input.onkeyup = function(e) {
if (e.keyCode == 13 && input.value != "") {
loading.classList.remove("hidden");
weather.getWeather(input.value, function (returnValue) {
for (iter in returnValue) {
weatherHolder[iter].classList.remove('hidden');
document.getElementById("weather" + (parseInt(iter) + 1)).innerHTML = returnValue[iter].date;
}
});
loading.classList.add("hidden");
}
};
})();
I want to force the execution of the line loading.classList.remove("hidden"); before waiting for the closure bellow to complete.
If I remove the closure lines the script works perfectly, however, I can't make it work if the closure fails.
For instance, the code below works perfectly:
(function() {
var weather = new Weather();
var input = document.getElementById("inputCity");
var weatherHolder = document.getElementsByClassName("weather");
var loading = document.getElementById("loadingSign");
input.focus();
input.onkeyup = function(e) {
if (e.keyCode == 13 && input.value != "") {
loading.classList.remove("hidden");
alert("teste");
loading.classList.add("hidden");
}
};
})();
The problem is in the line loading.classList.remove("hidden"); . This is supposed to remove a class that's hiding a message and a spinner. If I replace the closure lines with an alert the spinner shows, however, if I have that closure function the spinner is never shown.
How can I force that line to be called whether the closure is successful or not?

I don't really understand the question but judging from the code you have, it would be wiser to add the loading.classList.add("hidden"); inside the callback so it gets executed correctly.
(function() {
var weather = new Weather();
var input = document.getElementById("inputCity");
var weatherHolder = document.getElementsByClassName("weather");
var loading = document.getElementById("loadingSign");
input.focus();
input.onkeyup = function(e) {
if (e.keyCode == 13 && input.value != "") {
loading.classList.remove("hidden");
weather.getWeather(input.value, function (returnValue) {
for (iter in returnValue) {
weatherHolder[iter].classList.remove('hidden');
document.getElementById("weather" + (parseInt(iter) + 1)).innerHTML = returnValue[iter].date;
}
// Here
loading.classList.add("hidden");
});
}
};
})();
Ok so you are asking to "force the execution of..." but in fact what I suspect is happening here is that: the line we moved was not "waiting" on getWeather to finish.

Related

jQuery script works only once, then TypeError: $(...) is not a function

I've downloaded this script for use conditional fields in forms:
(function ($) {
$.fn.conditionize = function(options) {
var settings = $.extend({
hideJS: true
}, options );
$.fn.showOrHide = function(is_met, $section) {
if (is_met) {
$section.slideDown();
}
else {
$section.slideUp();
$section.find('select, input').each(function(){
if ( ($(this).attr('type')=='radio') || ($(this).attr('type')=='checkbox') ) {
$(this).prop('checked', false).trigger('change');
}
else{
$(this).val('').trigger('change');
}
});
}
}
return this.each( function() {
var $section = $(this);
var cond = $(this).data('condition');
// First get all (distinct) used field/inputs
var re = /(#?\w+)/ig;
var match = re.exec(cond);
var inputs = {}, e = "", name ="";
while(match !== null) {
name = match[1];
e = (name.substring(0,1)=='#' ? name : "[name=" + name + "]");
if ( $(e).length && ! (name in inputs) ) {
inputs[name] = e;
}
match = re.exec(cond);
}
// Replace fields names/ids by $().val()
for (name in inputs) {
e = inputs[name];
tmp_re = new RegExp("(" + name + ")\\b","g")
if ( ($(e).attr('type')=='radio') || ($(e).attr('type')=='checkbox') ) {
cond = cond.replace(tmp_re,"$('" + e + ":checked').val()");
}
else {
cond = cond.replace(tmp_re,"$('" + e + "').val()");
}
}
//Set up event listeners
for (name in inputs) {
$(inputs[name]).on('change', function() {
$.fn.showOrHide(eval(cond), $section);
});
}
//If setting was chosen, hide everything first...
if (settings.hideJS) {
$(this).hide();
}
//Show based on current value on page load
$.fn.showOrHide(eval(cond), $section);
});
}
}(jQuery));
I'm trying this because I need to use conditionize() in one of my tabs and when I reload the tab, all works but if I go to other tab and I return to the previous tab(where I need this works), I get that error.
When I change tabs, I'm only reloading one part of the page.
When I load the page this works perfectly, but if I try to call function again from browser console, it tells me that TypeError: $(...)conditionize() is not a function.
I have included the script in header tag and I'm calling it with this script on the bottom of body:
<script type="text/javascript">
$('.conditional').conditionize();
</script>
EDIT:
I have written
<script type="text/javascript">
console.log($('.conditional').conditionize);
setTimeout(function () {console.log($('.conditional').conditionize);}, 2);
</script>
and this print me at console the function, and when 2 milliseconds have passed, it print me undefined
I have found the solution.
Because any reason, the $ object and jQuery object are not the same in my code.
I have discovered it using this on browser console:
$===jQuery
This return false (This was produced because in other JS, I was using the noConflict(), which give me the problem)
Explanation: noConflict()
So I have solved it changing the last line of my JS by:
//Show based on current value on page load
$.fn.showOrHide(eval(cond), $section);
});
}
}($));
Putting the $ instead of 'jQuery'

javascript multiple functions at once

As I needed help here
#ryanpcmcquen offered great help, but as a "noob" at javascript I would like to know 2 more things
When I want to create another function how do I make it?
document.addEventListener('DOMContentLoaded', function () {
'use strict';
var unitBlock = document.querySelector('select#unit_block');
var unitRowBig = document.querySelector('select#unit_row_big');
var unitRow = document.querySelector('select#unit_row');
var unitColumn = document.querySelector('select#unit_column');
var unitSize = document.querySelector('select#unit_size');
unitBlock.addEventListener('change', function () {
if (unitBlock.value === 'A') {
unitRowBig.disabled = false;
unitRowBig[4].disabled = false;
} else {
unitRowBig.disabled = false;
unitRowBig[4].disabled = true;
}
});
unitBlock.addEventListener('change1', function () {
if ((unitRowBig.value === '1') && (unitBlock.value === 'A')) {
unitRow.disabled = false;
unitRow[8].disabled = true;
unitRow[9].disabled = true;
unitRow[10].disabled = true;
unitRow[11].disabled = true;
unitRow[12].disabled = true;
}
});
});
Because it doesn't seems to work my way.
No need to add a new event, besides change1 is not a valid event, you can find a list of events here:
https://developer.mozilla.org/en-US/docs/Web/Events
Just put that conditional inside the original event handler:
document.addEventListener('DOMContentLoaded', function () {
'use strict';
var unitBlock = document.querySelector('select#unit_block');
var unitRowBig = document.querySelector('select#unit_row_big');
var unitRow = document.querySelector('select#unit_row');
var unitColumn = document.querySelector('select#unit_column');
var unitSize = document.querySelector('select#unit_size');
unitBlock.addEventListener('change', function () {
// You may want to comment out all of this section:
if (unitBlock.value === 'A') {
unitRowBig.disabled = false;
unitRowBig[4].disabled = false;
} else {
unitRowBig.disabled = false;
unitRowBig[4].disabled = true;
}
// Down to here.
// Here's your code!
if ((unitRowBig.value === '1') && (unitBlock.value === 'A')) {
unitRow.disabled = false;
unitRow[8].disabled = true;
unitRow[9].disabled = true;
unitRow[10].disabled = true;
unitRow[11].disabled = true;
unitRow[12].disabled = true;
// Including an antithetical clause,
// to account for the user changing their mind.
} else {
unitRow.disabled = true;
unitRow[8].disabled = false;
unitRow[9].disabled = false;
unitRow[10].disabled = false;
unitRow[11].disabled = false;
unitRow[12].disabled = false;
}
});
});
Note that I also included the opposite disabled conditions in an else clause, in case the user makes one choice, and then changes to another.
In case you really need two separate functions (what is not the case here), just do it like this:
unitBlock.addEventListener('change', function () {
console.log('First event listener')
});
unitBlock.addEventListener('change', function () {
console.log('Second event listener')
});
document.addEventListener stores all the functions you sent to him, so when the change event will be fired, it will execute all of them, in the order you passed them to it.
In short, when the change event is fired, you will have:
> "First event listener"
> "Second event listener"
I hope this helped you!

AddEventListener function won't execute

The iFrameOn function runs on page load, and up until it is supposed to execute the iBold function is works fine. I've gone through and debugged as much as possible, and everything seems fine. When I output every variable to the console, the values are correct. It's just that one line (iBold(targetiFrame);) that won't run. I'm not sure what's going on.
function iFrameOn() {
var iFrames = document.querySelectorAll('form > iframe'); //Get all iframes in forms
var bolds = new Array(), italics = new Array(), underlines = new Array(), targetiFrame;
var getRT = document.getElementsByClassName('richText');
for (var rtIndex = 0; rtIndex < getRT.length;rtIndex++) { //Rich text event listeners
var rtid = getRT[rtIndex].id;
if (getRT[rtIndex].className == "richText bold") { //Bold text event listener
console.log('The id is: '+rtid);
bolds.push(rtid);
console.log('The bolds array contains: '+bolds);
} else if (getRT[rtIndex].className == 'richText underline') { //Underline text event listener
underlines.push(getRT[rtIndex]);
} else if (getRT[rtIndex].className == 'richText italic') { //Italic text event listener
italics.push(getRT[rtIndex]);
}
}
bolds.forEach(function(e, i, a) { //e = a[i]
console.log('e is '+e);
document.getElementById(e).addEventListener('click', function() {
console.log(e+' was clicked!');
targetiFrame = document.getElementById(e).getAttribute('data-pstid');
iBold(targetiFrame);
}, false);
});
}
function iBold(target) {
if (target == 0) {
document.getElementById('richTextField').contentDocument.execCommand('bold', false, null);
document.getElementById('richTextField').contentWindow.focus();
} else {
document.getElementById(target).contentDocument.execCommand('bold', false, null);
document.getElementById(target).contentWindow.focus();
}
}
I apparently had another iBold function in another js file

NotFoundError: DOM Exception 8 when substituting innerHTML

I'm new to js-development. I have the following code:
<html>
<body>
<div><span id="inline">Click here to start editing</span></div>
<script>
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
var value = inline_editable.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
inline_editable.onblur();
}
}
}
}
</script>
</body>
</html>
Which shows some text inside span and allows inline editing. When I finish editing within just onblur event it work perfectly fine. But if I want to terminate editing by Enter and use the same hander I get an error NotFoundError: DOM Exception 8 in this line:
inline.innerHTML = value;
Nevertheless everything works as I expect. Can anyone help me to avoid this error?
I assume that is happened because I destroy inline-editable element while event handling is not finished and it wants to invoke onchange maybe. Should I have 2 controls all the time an switch their visibility instead?
Problem here is the onblur is triggered twice, the second time, the element is not there which causes the problem. Kill the events
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
this.onblur = function(){};
var value = this.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
this.onblur();
}
}
}
}

GM_registerMenuCommand is not defined

everybody. I'm puzzled as to why I keep getting the error "GM_registerMenuCommand is not defined" when I try to run a userscript that I created. I have tried this in Firefox using Scriptish 1.0b9 and the latest version of Greasemonkey. I even disabled all addons except Scriptish to see if it was a conflict, but with no joy.
I'm including jQuery in my userscript using this template by Erik Vold. Before trying this template, I put the exact same code block in the template proposed by Joan Piedra and everything worked fine. Unfortunately, Piedra's template did not work in Chrome, which is something that I think is necessary, considering Chrome's growing userbase. The snippet that's throwing the error is below:
// a function that loads jQuery and calls a callback function when jQuery has finished loading
function addJQuery(callback) {
var script = document.createElement("script");
script.setAttribute("src", "http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js");
script.addEventListener('load', function() {
var script = document.createElement("script");
script.textContent = "(" + callback.toString() + ")();";
document.body.appendChild(script);
}, false);
document.body.appendChild(script);
}
// the guts of this userscript
function main() {
var isLevelupMove = false;
var isTutorMove = false;
var isTM = false;
var TMhead = $('#moves\\:machine');
var hasSecondEvo = false;
var hasFinalEvo1 = false;
var hasFinalEvo2 = false;
var header = $('.header-row').eq(1);
var TMmoves = new Array();
//This section deals with the user-defined colors
GM_registerMenuCommand("Color for pre-evolutionary-only moves", prevoColorPrompt);
GM_registerMenuCommand("Color for first evolution-only moves", evoColorPrompt);
if(localStorage.getItem('prevoColor') == null || localStorage.getItem('evoColor') == null)
{
localStorage.setItem('prevoColor', 'red');
localStorage.setItem('evoColor', 'orange');
}
var prevoColor = localStorage.getItem('prevoColor');
var evoColor = localStorage.getItem('evoColor');
function prevoColorPrompt()
{
var input = prompt("Please enter a desired 6-digit hex color-code for pre-evolutionary pokemon:")
localStorage.setItem('prevoColor', '#'+input);
}
function evoColorPrompt()
{
var input = prompt("Please enter the desired 6-digit hex color-code for first-evolution pokemon:")
localStorage.setItem('evoColor', '#'+input);
}
//This loop tests each 'th' element in a sample header row, determining how many Evos are currently present in the chart.
$('.header-row').eq(1).find('th').each(function(index)
{
if($(this).find('a').length != 0)
{
switch(index)
{
case 2:
hasSecondEvo = true;
break;
case 3:
hasFinalEvo1 = true;
break;
case 4:
hasFinalEvo2 = true;
break;
}
}
});
//All 'tr' siblings are TM moves, since it's the last section on the page
//This array puts only the names of the available TMs into the TMmoves array
TMhead.nextAll().each(function(index)
{
TMmoves.push($(this).children(":first").find('a').eq(0).html());
});
$('tr').each(function(index)
{
var moveName = $(this).children(":first").find('a').eq(0).html();
moveName = $.trim(moveName);
switch($(this).attr('id'))
{
case 'moves:level-up':
isLevelupMove = true;
break;
case 'moves:egg':
isLevelupMove = false;
break;
case 'moves:tutor':
isTutorMove = true;
case 'moves:machine':
isTM = true;
}
if(isLevelupMove || isTutorMove)
{
var babyMoveCell = $(this).find('td').eq(0);
babyMoveText = $.trim(babyMoveCell.html());
secondEvoCell = babyMoveCell.next();
secondEvoText = $.trim(secondEvoCell.html());
finalEvo1Cell = secondEvoCell.next();
finalEvo1Text = $.trim(finalEvo1Cell.html());
finalEvo2Cell = finalEvo1Cell.next();
finalEvo2Text = $.trim(finalEvo2Cell.html());
//This checks if evolutions have checkmarks
if(babyMoveText.length > 0)
{
if(hasSecondEvo && secondEvoText.length == 0 || hasFinalEvo1 && finalEvo1Text.length == 0 ||
hasFinalEvo2 && finalEvo2Text.length == 0)
{
//See if the move is a TM before proceeding
var tm = tmCheck(moveName);
if(!tm)
{
if(secondEvoText.length > 0)
{
babyMoveCell.css("color", evoColor);
secondEvoCell.css("color", evoColor);
babyMoveCell.prev().find('a').eq(0).css("color", evoColor); //highlights move name
}
else
{
babyMoveCell.css("color", prevoColor);
babyMoveCell.prev().find('a').eq(0).css("color", prevoColor);
}
}
}
}
else if(secondEvoText.length > 0)
{
if(hasFinalEvo1 && finalEvo1Text.length == 0 || hasFinalEvo2 && finalEvo2Text.length == 0)
{
var tm = tmCheck(moveName);
if(!tm)
{
secondEvoCell.css("color", evoColor);
babyMoveCell.prev().find('a').eq(0).css("color", evoColor);
}
}
}
}
});
function tmCheck(input)
{
var isTM = false;
//Iterate through TMmoves array to see if the input matches any entries
for(var i = 0; i < TMmoves.length; i++)
{
if(input == TMmoves[i])
{
isTM = true;
break;
}
}
if(isTM == true)
return true;
else
return false;
}
//alert("evoColor: " + localStorage.getItem('evoColor') + ". prevoColor: " + localStorage.getItem('prevoColor'))
}//end main()
// load jQuery and execute the main function
addJQuery(main);
This is the userscript I'm trying to implement this for. If anyone has any suggestions or ideas about why I'm getting the error, I'd love to hear them!
This does not work because, if you look carefully at what addJQuery does, you'll realize that it injects the code of the function you pass it into a script element that gets appended to the end of the body element.
This means that you're now working in the same space as the scripts the website has, so all GM_* are not going to be available. What you can do is to move some of the code which require those functions to outside the main function, but remember that the Greasemonkey sandbox means that code running inside the main function cannot communicate with code outside it directly. You can have indirect communication, through for example watching DOM manipulation, or even unsafeWindow, but looking at your code it does not appear to be easily separateable.
This approach will not work because addJQuery() is not transferring workspace objects to the page's scope, it's essentially recreating your code from the source.
That means that the GM_ functions are not usable because there is no link between the sandbox and the copy of the code that addJQuery() made.
If your script needs GM_ functions, then just use straight GM code with the // #require directive for things like jQuery. Your only option for Chrome is Tampermonkey.
In both cases, addJQuery()-like tricks are not needed.

Categories

Resources