How can I tell what I am iterating over in jQuery? - javascript

When I am debugging jQuery code, how can I tell what elements my selector is operating on?
I just spent a couple of hours trying to figure out what I was doing wrong with this code that is supposed to turn off one input field and turn on another:
<input id="qty-dropdown" ...>
<input id="qty-box" ...>
...
$('.qty-dropdown').hide();
$('.qty-box').show();
and of course the problem was that it should have been
$('#qty-dropdown').hide(); // # for ID, not . for class
$('#qty-box').show();
I was working on figuring if I was calling hide() and show() incorrectly, and not realizing I was operating on zero elements. If I had been able to tell that $('.qty-dropdown') was selecting no elements, I'd have saved a lot of time.
Is there anything I can do like:
$('.some-selector').dump()?
that will give me debugging information?
This question has an answer with a chunk of code that looks like it should dump into the console, but dropping that into my code didn't seem to put anything into my Firebug console.
I also realize that my original problem could have been solved with:
alert( $('#js-qty-dropdown').length );
but I am looking for a more complete solution for next time.

jQuery objects act kind of like arrays. Get the number of elements selected:
$('.qty-dropdown').length
If you are using Chrome for debugging, you can log out the object and hover over the different elements, and it will highlight them on the page:
console.log($('.qty-dropdown'));

Related

Selenium : I have a webpage where two elements have same Xpath, even index doesn't separate them apart

They both get identified when the index is [1], and for index [2] nothing gets identified. The only thing which separates them is that the first popup has display:none, and the other popup has display:block, but they do not get a play in deciding this particular fields because Xpath, because these fields are fetched from some different source.
Alas, these divs even though they have same elements, they have a document attribute so, the Xpath for these don't start with this div, but after the part of the document. I have racked my brain all I could, I could give you a vague idea of how the code looks like below.
The code looks like:
<div name='some-name' style="display:none;">
...
<!document>
<div id='some-id'>
....
<button name='some-name-2'>some-button-name</button>
....
</div>
...
</div>
<div name='some-name' style="display:block;">
...
<!document>
<div is='some-id'>
....
<button name='some-name-2'>some-button-name</button>
....
</div>
...
</div>
So both of their Xpath looks something like:
[id='some-id']/...../button
Giving ([id='some-id']/...../button)[1] fetches both the elements.
Giving ([id='some-id']/...../button)[2] fetches none of them.
Update
I had two iframes in this case, and since I had to switch between them, it was not getting detected. Thanks, I fixed this.
The way your Xpath reads is to find the element with id='some-id' and then find the first or second button within that. If you change it so that the [1] and [2] are placed after the [id='some-id'] rather than at the end of the path you can choose to find the first or second [id='some-id'], and then the button within that element.
Unless the two elements are actually one element, then there will always be XPath expressions that select one and not the other. In fact, there are an infinite number of such expressions. The trick is in finding a good XPath expression, and that depends on how much of the page content is fixed, and how much is variable (if the data is completely fixed, then you don't even need to read it, because you already know everything about it).
I think your inference that the subscripts [1] and [2] don't work is incorrect. It would be useful to know what expressions you actually used, because what you have written in your post:
([id='some-id']/...../button)[1]
is not a valid XPath expression at all.

NIghtwatch: I need to search for a word in a table without using document

I'm a beginner in using Nightwatch and I'm stuck with the following:
I've got a table that has a <tbody> and multiple <tr>s in it. I need to check if a specific word is in one of the cells in the first column.
First, I tried getting the whole table using document.GetElements... but it appears this is not supported by Nightwatch. Currently, I'm looking for a solution without using Nightwatch custom command.
P.S. I can't create tags yet, so it'd be awesome if someone could create one, like table-to-array or something like that.
You can execute javascript code into the browser using execute command (Documentation here). So you can do something like this:
client.execute(function(){
/*your js to get the DOM element and check if the element has the text you want*/
return true //or false if didnt find it
},[],function(response){
console.log(response.value)//this will contain your returned value
client.assert.equal(response.value, true, "Text not found on table")
})

Trying to fix table header, getting css issue

Initially I asked this Question and written my own plugin to achieve the same,But i am facing very strange issue regarding to css of table.
After applying the plugin table cells borders are getting dis-sorted.
jsFiddle of the problem: Problem demo
In fiddle you can see that after first cell of the first tr, the header border line and table border line don't line up. I want the border line of thead cells and td cells to line up.
Can anyone tell me how to achieve that?
Lets start by doing a bit of a clean up of the code you posted so I can actually read your code and maintain a firm grasp on reality while we go down this rabbit hole.
If you write clean code, your problems will be exceptionally easier to see.
So lets clean it up and watch as all the problems here reveal themselves.
Step one: Your jsFiddle sets it to run "onDomReady," which basically means you've got $(document).ready(...) calling all the code in the box, which is fine, except you're got another got $(document).ready(...) inside there. Lets change that.
Step two: Lets add some white space and proper indentation in there, and stop using these one letter variable names.
{} are scope brackets, they should indent, not cover everything, they let us know what part of scope something is in.
Don't write .each() loops on one line, this adds no value and makes your code confusing to read.
$t should be called something meaningful, lets try element, because it holds the $(this) element, which is the active element you're working with.
w should be called something meaningful, but since you only use it twice I'm just going with element.width().
o needs to be less ambiguious, lets go with obj.
Step three: Selection structures
if(typeof(i)=='number')o.height=i;
else if(typeof(i)=='object')o=i;
else if(typeof(i)=='undefined')o={height:300}
Break that up, make it readable. Saving lines doesn't make you a better program, writing clean and easily understandable code will.
Why not use the switch-case statement?
switch (typeof(i)){
case "number":
o.height=i;
break;
case "object":
o=i;
break;
case "undefined":
o={height:300};
break;
}
Step four: Don't in-line styles. Just don't. There's no reason to do it, and it makes everyone's life harder.
Instead, lets just place the styles gently into the style sheet where it belongs, and make the parent=$('...') line look like parent=$('<div><div></div></div>').appendTo('body');.
Step five: Closure doesn't pass any value to arguments
After a bit of clean up, we see this block of code:
self.width(self.width() -
function(width){
var parent,child;
if(width===undefined){
parent=$('<div><div></div></div>').appendTo('body');
child=parent.children();
width= (child.innerWidth()) - (child.height(99).innerWidth());
parent.remove();
}
return width;
}()
);
Okay, that's a problem. Lets cut out a few lines to point out the problem here:
self.width(
self.width() -
function(width){
/*...*/
if(width===undefined){
/*...*/
}
return width;
}()
);
So, a quick refresher on this pattern you have here:
(function(arg1){
/*code*/
})(data);
Data gets passed to arg1. Arg1 declaires a variable in the scope local to that function, it doesn't get anything from outside. Outside data is passed in through the set of () that call the function, which your code had left abandoned. Think of it this way:
var msg = function(text) {
alert(text);
};
Then you call it as...
msg("hello world");
What your closure is doing is almost the same thing, except where you define your function, you also call it. Thus...
(function(text) {
alert(text);
})("hello world");
So, you need to pass a value of some sort into there, other wise this whole thing is always undefined. Lets do that. What are we passing? I have no way to be sure. This is why programmers need to add comments to their code.
Step six: Comment your code so people other than yourself will look at this code and have not a damn clue what you truthfuly wanted to do, and can only guess. It's like you posted a 200 point bounty and didn't bother helping people who want to help you. Why are you doing this to yourself, dude? Why couldn't you just go //This is what this does to give me a hint? What did I ever do to you?
Step Seven: Lets see if we can make the JS changes work with the JS Fiddle
Great odin... that HTML's 2000 lines long?
Okay, I'm working with pastebin here for the sake of saving space in the post here.
Alright, you started off with this: http://pastebin.com/xjmm4cev
You're using a lot of no-wrap, and putting classes onto individual elements. You shouldn't have to do this on each HTML element, CSS takes care of that very effectively, so lets go ahead and just rip out all the nowrap=nowrap and class="header" stuff (we'll put it back in a moment, but only ONE per group, not each element).
Then lets get rid of the useless blank lines.
Lets run this through HTML tidy and get it nice and indented correctly.
http://pastebin.com/uHtSZ4h5
Much easier to read over. Okay, so what do we see here? Well, it looks like you keep going in circles, cutting and pasting the same thing again and again. You also in-line javascript such as using onchange and onclick attributes. This is generally an awful thing to do to your code, and makes it hard to maintain (as I'm sure you've seen with this 2000+ line beast of cut+paste 27 times in a row).
So, lets take a look here:
elements, not inside a form
Elements in a table that are outside of rows, but not head/body/footer sections of a table
Code that's a huge pain to maintain because it keep going in circles, if you need to change this, you're basically screwed.
Lets fix all that.
We're going to use events in the tags, rather than in-line things. So, all of those in-line onchange and click attributes get the boot.
All of these inputs that are just floating around need to get put into a form, and taken out of this place in tables that only rows or table sections belong in.
Figure out how we can not have excessive input elements, if we can help it.
What the heck is the )="" that you have on every input button? Deleted.
So, here's all of your hidden boxes: http://pastebin.com/LXZSkvyf which I've removed, because we don't have a anywhere.
And here's what the code looks like without all of these weird things in it: http://pastebin.com/MiaJTGpb
Much more readable, but still not quite there.
Step 8: What can you do to make the HTML of the table work better?
You're using Thead and Tbody, and that's good.
You're using attributes for things like cellpadding, that's bad.
You've given each body row an ID. I don't feel you needed to do this, but it's not always bad. However, I'll show you how you could work without it.
You give some selects a select-box class, but it's nowhere in the css. I've removed it.
You give a title attribute to the selects that says "option_value". The title attribute is generally used to make a tool-tip popup when you leave the mouse over something. I'm not sure what you're going to do here, but that's bound to confuse your user. I'd highly suggest giving something better than option_value in that place.
You keep using the ID Submit_FMS_AddDelivery. HTML ID tags are meant to be unique, and used only once on one element. You've got it 27 times, that's bad. I don't think you need an ID on it, so I've removed it.
You've also got input-btn going on, I'm removing it, because you haven't shown it's used anywhere.
You probably don't need any of these ID tags on select and TR to be honest, so I'm pulling them.
What's that look like? Basically, you've got what's almost just the data, in it's nice pure form. That's good. http://pastebin.com/UNS6CAtb
Step 9: What were you trying to do?
Lets step back and take a look here.
All you really wanted to do was keep a fixed header, but you've ended up doing a lot of JavaScript hacks and manipulating the DOM in a lot of places. We need to stop doing this. Is there a simpler way to do what you want?
I'll have to expand on how to make this function with the rest of your stuff, but I get up for work in a few hours. I'll update again with more... But we'll get there.
Right now, because I haven't fixed what I took out, it doesn't look right. But I'm tired.
So, here's where we are so far: http://jsfiddle.net/5C6z7/
Plus those inputs we took out (and will be going back in later, in a different way)
Looks like all you have to do is to take the padding into account: each cell has 3 px padding both left and righ so you have to add 6 px to the width:
$t.find('tr:first th').each(function(){cols.push($(this).width()+6);});
Otherwise the cells with only one word inside will "push" the actual width a bit wider so that the word will fit and other cells with space to move will compensate by becoming a bit narrower. The header and the body both do this independently with different contents which creates the difference in actual cell widths.
EDIT: For Firefox you also need to widen the table so that the cells fit. After calculating the column widths add
var actualWidth = $t.width()+cols.length*6;
$t.width( actualWidth );
And later change the wrapper to:
$wrap.css({width:actualWidth,height:o.height,overflow:'auto'});
EDIT 2: To have both the header and the body scroll simultaneously you need to wrap them both to an outer div that handles the scrolling.
var $outerWrap = $( '<div>' ).css( {width:"300px", overflow:'auto' } );
var $wrap=$('<div>').css(
{ width:actualWidth,height:o.height,"overflow-y":'auto', "overflow-x":'hidden' }
);
$firstRow.wrap( $outerWrap );
$firstRow.after( $wrap );
$wrap.append( $t );
Demo: http://jsfiddle.net/YcRTz/2/
What about replacing '<th>' tags with '<td>' tags?
No extra js code required.
http://jsfiddle.net/spQAh/7/
Okay so instead of troubleshooting or using anything complex, I came up with simple CSS + JS solution to your problem. Take a look at http://jsfiddle.net/TdLQT/
Obviously, the process of making header static, can be made dynamic, meaning trigger it after user scrolls a bit or have it there by default or count other object's position from top window and trigger static behavior when it reaches or exceeds certain pixels. I can provide that if you give me details on exact design of your HTML page.
Anyways, I as you can see, have used fixed pixel heights which you can choose not to or make them elastic or dynamic. What is important to know is that, though solution relies on JS a bit, output position is purely from CSS. I am using classes to manipulate the position.
the simple solution is that when you calculate the width of the column you do not include padding. So you need to change the line
$t.find('tr:first th').each( function() {
cols.push($(this).width());
});
to
$t.find('tr:first th').each( function() {
cols.push($(this).outerWidth());
});
if your cells have margins use .outerWidth(true)
This works in quirks mode with IE7&8 and Chrome IE8 has a problem in strict mode. However I believe that that problem is caused by the added scrollbar. If you want to use IE8 in strict mode you have to allow for the width of the vertical scrollbar..

Invoking jQuery function without an element

So, I use jQuery quite extensively and I am well aware of the "right" way to do the below, but there are times where I want to solve it in a more generic way. I'll explain.
So, I may have a link, like this: <a href='menu' class='popup'>Show menu</a>. Now, I have a jQuery function that fires on click for all a.popup that takes the href-attribute and shows the <div id='menu'></div> item (in this case). It also handles URL's if it can't find a DOM item with that ID.
No problem here. But, there are times when I don't have the same control over the coe where I can create a selectable target that way. Either because the code isn't created by me or because it is created through a chain of function that would all need a huge ovrhaul which I won't do.
So, from time to time, I would like to have this code:
Show menu
This would be in a case where I can only submit the label and the HREF for a link. No class, no nothing.
Problem here is that the function popup() has no idea about what element invoked it, and in most cases that's not a problem for me, since I only need to know where the mouse cursor was upon invokation.
But in some cases, I use someone elses jQuery functions, like qTip or something else. so I still want to fire off qTip(); when clicking a link that runs this JS function, but what do I attach it to to make it show? I can't just runt $().qTip(); because that implies $(this) and "this" is undefined inside the function.
So how do I do it? Any ideas?
Is there anyway you change the javascript method to javascript:popup('menu', this);? I've used this method successfully many times.
Instead of referring to "this" try referring to $('a:focus') to refer to the link that was clicked.
Here's a quick and, as #Crescent Fresh would add, dirty (☺) sample:
<body>
<p>Show popup()</p>
<div id="menu" style="display:none">Today's menu</div>
<script type="text/javascript">
function popup(elm) {
$('#' + elm).show();
alert( $('a:focus').text() )
}
</script>
</body>
I tried just ":focus" but IE7 returned too much content. I tested this in FF 3.6.3, IE7, Chrome 4.1.249.1064 (all on Windows) and it seems OK, but I see now (when I was just about to hit "Post Your Answer") this relies on the browser's native support for querySelectorAll - see this jQuery Forum post ":focus selector filter?" and the jQuery.expr entry in the jQuery Source Viewer (where it appears Paul's idea was not implemented).
How about
Show menu
Once you get the event object you can virtually do anything to it.

Changing input text value with Javascript doesn't change display

I'm trying to change the value of a text input field based on user actions. I'm doing it like this:
document.getElementById(textFieldID).value = newValue;
It isn't quite working -- the original text in the field remains on the screen, unchanged. However, when I submit the form, it behaves as though the value was indeed changed correctly. (And a debug alert confirms that yup, I'm hitting that bit of the code and passing in the right field ID and text value.) Anybody have any insights? Is there something I need to be doing to redraw the input element?
Edit: Per Jeff B's request, and per the fact that this seems to have everybody stumped, here's some relevant bits of code:
<script LANGUAGE="JavaScript" TYPE="text/javascript">
function changeText(changeSelector)
{
var myindex = document.getElementById(changeSelector+"Recent").selectedIndex;
var SelValue = document.getElementById(changeSelector+"Recent").options[myindex].value;
document.getElementById(changeSelector).value = SelValue;
document.getElementById("historicalText").value = SelValue;
document.getElementById("historicalTextSelect").value = changeSelector;
}
</script>
<input onChange="updateScrollingPreview1217(this); return true;" type="text" id="crawlMsg1217" name="crawlMsg1217" size="60" maxlength="1000" value="">
<select id="crawlMsg1217Recent" name="crawlMsg1217Recent" onchange="javascript:changeText('crawlMsg1217');">
[options go here]
</select>
And that "onChange" handler isn't what's gumming up the works; I get the same behavior with or without it.
Edit 2: It looks like the problem is being caused by "JSpell", a third-party spelling checker our product uses. (I'm told that clients prefer using it to a spellcheck built into the browser; go figure.) It appears to be slightly misconfigured on my test machine, so I'm going to try straightening that out and praying that it makes the problems go away. If it doesn't ... should be interesting.
Edit 3: Yup. Fscking JSpell. Just posted a complete answer for the sake of posterity, will accept it tomorrow when I'm allowed. My thanks to everybody who tried to help; +1's all around, wish I could give more.
I have confirmed that the culprit is indeed JSpell, and that the precise trouble spot is this line:
window.onload=jspellInit;
Despite the prayers mentioned in Edit 2 above, making sure it was configured correctly did NOT make the problem go away. And this line is indispensable to JSpell's functionality. I don't know if JSpell always hoses Javascript functionality this way, or if there's some sort of perfect storm of factors that's causing it to pick a fight with my page, but that is indeed the source of my problems.
My thanks to everybody who tried to help. This was obviously a bit of a no-win in terms of getting the right answer, since it was caused by a component that was seemingly entirely unrelated and thus didn't get mentioned by me, but you at least confirmed that I was (in theory) doing things right and not simply going insane.
Is the document's id actually "textFieldID" or is "textFieldID" a javascript variable that contains the ID of the text input to change? If it is not a variable, I believe you should make it:
document.getElementById('textFieldID').value=newValue;
It's hard to debug this without the context, since the code you have ought to work fine. Can you confirm that you've got the right node by doing something like:
document.getElementById(textFieldID).style.border = "4px solid red";
What does any other element on the page have a name attribute that is the same as the id?
Internet Explorer 8 and later. In IE8
mode, getElementById performs a
case-sensitive match on the ID
attribute only. In IE7 mode and
previous modes, this method performs a
case-insensitive match on both the ID
and NAME attributes, which might
produce unexpected results. -
http://msdn.microsoft.com/en-us/library/ms536437%28VS.85%29.aspx
Try alerting your the nodeName and id ofr the returned element and make sure its the input you expect.
Use div element instead of textfield. I had same problem, my textfield which is changed with another script wasnt get the right value. you can easily use any div element like textfield with some CSS. than you can get the value from div using innerHTML.

Categories