I'm in the process of redesigning a menu/submenu - just moving the submenu from somewhere in the bottom of the page to under its corresponding menu item. Right now, the same submenu (actions) is found twice in page, only that one of the times it's hidden and displayed with jQuery on mouse over. In both menus I have an option pointing to a function with a try...catch block.
Here's the problem, when using the jQuery-displayed actions, Javascript crashes in the try block ("something" is undefined - classical), just like the word 'try' wasn't even there. I've used the firebug debugger, and the same error occurs when using the other submenu, but that was just skipped and the code from inside catch was executed.
I just don't understand how this is possible, given that both options call THE SAME function, not 2 "identical" ones...
Any thoughts? Thanks :)
EDIT I:
function export_pdf() {
try {
ReportType=document.forms["down"].elements["ReportType"].value;
}
catch (e) {
ReportType='TDR_Report';
}
//other code...
}
And the error is "document.forms.down.elements.ReportType is undefined"...
Thanks!
EDIT II:
Calling the function:
onclick="export_pdf();"
In this case, I would simply try to avoid the try .. catch and test the ReportType. If the ReportType is undefined, set the value manually.
function export_pdf() {
if (document.forms["down"].elements["ReportType"] === undefined){
ReportType = 'TDR_Report';
} else {
ReportType = document.forms["down"].elements["ReportType"].value;
}
//other code...
}
Related
I have a wrapper for the HighCharts lib which autogenerates some code based on their API. In order to autogenerate this code I must export the HTML of the API website with ALL (recursive) the links (from the left side menu) expanded. This must be done recursive as new expanded links may have more not-yet-expanded links.
Right now I must manually proceed this loop from the Browser's Javascript console:
$('div.collapsed').find($('a.plus')).click();
$('div.collapsed').find($('a.plus')).length. If zero, I am done. If none zero, then proceed again with 1).
I tried to automatize this like follows:
while ( $('div.collapsed').find($('a.plus')).length !== 0 ) {
console.log('Doing a pass');
$('div.collapsed').find($('a.plus')).click();
console.log('Pass finished');
}
But it doesn't work as it goes to an endless loop. I guess this is because of onClick fires some async code (maybe an Ajax call?). Any idea how can I make it work?
Thanks in advance,
$('div.collapsed').find($('a.plus')).length is not going to change the value so please use
$('div.collapsed').find('a.plus').each(function(){
//some code
})
for more information regarding each. please check here
I finally fixed it this way:
/* Technically we could simulate the click on the links, like this:
$('div.collapsed').find($('a.plus')).click();
But that won't work as the clicks fire an async AJAX call so we don't know
when to expand their children. To make the recursion to work, I found there are many useful functions
in http://api.highcharts.com/resources/js/api.js
The function toogleExpand() allow us to pass a callback. That way, on callback, we expand again with children
making the recursion to work. */
function expandLinks( items ) {
if (items.find('div[id$="-menu"]').length !== 0) {
items.find('div[id$="-menu"]').each(function(){
var link = $(this);
toggleExpand(link.closest('.collapsed'), function() {
/* note that in this case we are making the recursion but only with the children of items */
expandLinks( items.find('div.menuitem.collapsed') )
});
});
} else {
if( $('div.collapsed').find($('a.plus')).length == 0 ) {
/* If there are no more links to open it means everything is ready so lets download the file */
downloadDetailsDivContent();
}
}
}
I use this code:
var x = require('casper').selectXPath;
...
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
this.test.assertExists(x("//a[contains(#id,'cell_13_1')]"), 'Clickable');
this.click(x("//a[contains(#id,'cell_13_1')]"));
});
I am trying to use if-else with assertExists to click another element if the first is not there:
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
if(this.test.assertExists(x("//a[contains(#id,'cell_13_1')]")==="PASS"){
this.click(x("//a[contains(#id,'cell_11_1')]"));}
else{
this.click(x("//a[contains(#id,'cell_22_1')]"));
}
});
But that does not seem to work. How would one do it correctly?
That's exactly what casper.exists() is for. You can also explicitly pass or fail some things:
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
if(this.exists(x("//a[contains(#id,'cell_13_1')]")){
this.test.pass("Clickable");
this.click(x("//a[contains(#id,'cell_11_1')]"));
} else {
this.test.fail("Clickable");
//this.click(x("//a[contains(#id,'cell_22_1')]"));
}
});
This code is equivalent to your first snippet. Comment the fail() call and uncomment the last click in order to get your "intended" behavior.
Btw, it doesn't make sense to fail some assertion and still continue with the script. You have to think about what exactly you want to test and what part of your script is supposed to be navigation to the component under test.
I have two buttons. Save and undo. They execute a javascript, and in the end they call resetButtons() which is meant to disable the buttons until any other change in the users input occurs. This code has worked for over a year now, but while implementing some new features, this one has broken.
Here is a direct snippet of code:
function undo(){
var r = confirm("Are you sure you want to cancel your inputs?");
if (r == true) {
setLoadedValues(); //calls in the cached previous values
updateAllBoxValues(); //updates view
console.log("before resetting buttons");
resetButtons();
console.log("after resetting buttons");
}
}
function resetButtons(){
console.log("resetting buttons");
$(".save").addClass("disabled");
$('.save').attr('disabled', 'disabled');
$(".undo").addClass('disabled');
$('.undo').attr('disabled', 'disabled');
console.log("done resetting buttons");
}
This gives the following output:
before resetting buttons
after resetting buttons
I have suspected some wrong formatting with brackets, but this is a direct snippet of code, and if undo() works, so should resetButtons(), right? Also, I should have been given a undefined function, or an unexpecetd end of input or something similar if that was the case. The call to resetButtons is also ignored when I click the save button. I am lost. Help?
You have a duplicate function definition. Rename one of them.
Your code seems fine, you must have some other error somewhere else (like a function named like those two).
By the way, I suggest using jQuery's "prop" method to change the disabled attribute:
$('foo').prop('disabled', true);
$('foo').prop('disabled', false);
Try triggering the resetButton() function in console, if there's an issue it should return it. Testing it in a random console gives all the logs it should, but I can't vouch for the selectors. The error is probably elsewhere in the code.
I am using uitest.js (built-on Jasmine.js) to test our Kendo UI Mobile application. I am displaying an actionsheet and programmatically selecting on of the options. This works ok in the app but throws an error that fails the test.
I am using an action sheet like this:
<ul data-role="actionsheet" id="marketplace-price-actions" >
<li class="km-actionsheet-title">Select Price</li>
<li>$$$</li>
<li>$$</li>
<li>$</li>
</ul>
and In my spec I am selecting one of the options like this:
$("#marketplace-price-actions li a").eq(2).mousedown().mouseup();
and this works, yet throws the following error:
TypeError: "undefined is not a function"
I have created a jsfiddle that displays this at http://jsfiddle.net/zkent/DD6vj/2/. Be sure to open the console.
EDIT Based on the selected answer, the error was from passing values to the functions. I chose to create separate callbacks. See http://jsfiddle.net/zkent/DD6vj/4/.
It looks like you're not supposed to pass parameters to your action. I'm not sure why it's implemented this way, but this is causing your error (which also happens if you simply click on it, so it's not related to Jasmine).
As far as I can see, you have three options:
Use a separate callback for each item
Modify the ActionSheet source code to supply the clicked element to your action
Use a closure over the value you pass and return a handler
Option 3 seems to be the best solution if you only need to pass one value but want to avoid code repetition due to multiple handlers.
Please note that I haven't tested the following solutions at all, so use at your own risk.
For option #2, something like this might work:
kendo.mobile.ui.ActionSheet.fn._click = (function (click) {
return function (e) {
if (e.isDefaultPrevented()) {
return;
}
var action = $(e.currentTarget).data("action");
if (action) {
kendo.getter(action)(window)({
target: this.target,
context: this.context,
element: e.currentTarget // pass in the element that was clicked on
});
}
e.preventDefault();
this.close();
}
})(kendo.mobile.ui.ActionSheet.fn._click);
That way you'd at least know which element was clicked on and you could add data attributes to pass data, if you wanted, e.g.:
<li>$$</li>
which you could then read in your handler:
function alertme(e) {
console.log(e);
console.log($(e.element).attr("data-value"));
}
(demo)
For option #3, you would simply define your action as:
function alertme(val) {
return function(e) {
console.log(e);
console.log(val);
};
}
and your element would be as it was:
<li>$$
(demo)
I'm relatively new to javascript so please hold it against me.
I have a bit of code which should give the user a little time to reach the submenu from the base-menu.
My problem is that the code keeps executing in a weird order.
Here is the code:
function onFocusOut() {
var tester = 0;
setTimeout(function(){menuReset(tester)},1000);
}
function menuReset(tester) {
var hoverCheck = function (event) {
alert("#navBase a has focus"); //is fired, but to late...
var tester = event.data.varTester;
var tester = 1;
};
jQuery('#navBase').on('mousemove', 'a', { varTester: tester }, hoverCheck);
jQuery('#navBase').off('mousemove', 'a', { varTester: tester }, hoverCheck);
alert(tester); //This keeps firing first, before the alert in hoverCheck
if(tester == 1){
alert("tester = 1");
return;
}
else {
jQuery('#navBase ul').hide();
jQuery('#navBase').css({'width': ''});
jQuery('#navBaseAnchor').css({
'width': '', 'color': '',
'font-size': '',
'border-bottom-style': '',
'border-bottom-width': '',
'border-bottom-color': ''});
tester = 0;
}
}
Now I keep getting the alert that "tester" is 0, before the hoverCheck function is executed (which should set "tester" to 1) and fires the alert within that function.
Could someone tell me what I'm doing wrong?
I am also fairly new to JS, but should you also be watching out for variable scope errors too?
You have declared tester locally in onFocusOut() and in menuReset(tester), and then called it as a global var outside?
From answers.oreilly.com
LOCAL - Are those that are specific to a function and only work on it.
GLOBAL - Are those that are not defined within a function and may also serve to functions unless the function has not required that
variable.
Nevermind people...
I found a way around it all.
Currently i'm setting a .focus() to the anchor involved on mouseOver. (and of course blur() on mouseleave)
Then it's real easy to check the currently focussed element using document.activeElement.
So problem solved, altough in a bit different way.
alert(tester) is the first line of code that is executing something you notice as a user. The two function calls jQuery().on() and jQuery().off() are only attaching event handlers. If you want to see a "1" in the alert, you have to quickly move your mouse before hoverCheck is executed. But probably you cannot move your hand faster than JavaScript reaching the next line, which is the alert() with tester equals "0".
A little bit different approach would be to set a Javascript timeout() to make the submenu disappear after a certain amount of time if a certain condition isn't met.
Check out this JSFiddle example
Best of luck!