Suppose I have the following XML
<a>
<b></b>
<c></c>
<d></d>
<e></e>
</a>
If I want to get a all children of a except d. For that, I would use the following:
/a/*[not(self::d)]
However, this would return b, c, and e in three separate nodes. What I am looking for is only one node, containing b, c, and e, excluding d. It would be something simlar to the following pseudo code:
/a/* and <do not include /a/d please>
Is this possible?
Thanks.
XPath - no matter whether version 1.0, 2.0 or 3.0 - can only query subtrees and is not capable to modify (which includes removing) or construct new elements.
You would have to use XQuery or XSLT for this purpose, or modify the document outside the query language and only use XPath to select the result subtree, afterwards use another XPath to select the nodes you do not want and remove them from the result in the programming language you're using.
Related
I'm having to deal with some graphic items in a page that have <g> tags. I can do something like this to find them by dropping into selenium webdriver:
browser.wd.find_elements( :tag_name => "g" )
What would be the equivalent for watir webdriver?
Also, how would I convert those selenium elements into watir elements?
Could I add support for a <g> tag to watir locally somehow?
Solution 1 - Watir-Webdriver Equivalent:
The equivalent to what you were doing in selenium-webdriver is:
browser.elements( :tag_name => "g" )
So you can do something like this to output the text of each element:
browser.elements( :tag_name => "g" ).each do |x|
puts g.text
end
Solution 2 - Add Support for G Element:
After requiring watir-webdriver, add the following code:
module Watir
module Container
def g(*args)
G.new(self, extract_selector(args).merge(:tag_name => "g"))
end
def gs(*args)
GCollection.new(self, extract_selector(args).merge(:tag_name => "g"))
end
end
class G < Element
end
class GCollection < ElementCollection
def element_class
G
end
end
end
Then you can treat 'g' like any other element. For example:
puts browser.g(:index, 0).text
browser.gs.each{ |x| puts x.text }
The G and GCollection classes will support all the standard element methods. You can add additional methods to the class if there are things specific to that element.
Update - Example of Adding Custom Methods:
To get the cursor style, you would add a method to the G class like this:
class G < Element
def cursor_style()
assert_exists
return #element.style("cursor")
end
end
This will then allow you to get the cursor property like this:
puts browser.g(:index, 0).cursor_style
#=> move
Any custom methods that interact with the element need to start with assert_exists. Then, within the method, you can work with the element using the #element variable.
Note that because the G element inherits from the Element class, you could also have used the built in style method:
puts browser.g(:index, 0).style("cursor")
#=> move
There is no support for non-standard HTML tags in watir or watir-webdriver. In part because the list of possible tag names we'd have to support is endless.
You could monkeypatch in your own custom tags if you want to. In the long run, if you are going to have to deal with those custom tags a lot, that might be your best solution in terms of having stuff that acts just like other standard HTML elements supported by the Watir api.
You can use a CSS selector on the tag name, but that only works if you stick to element objects, as described in Watir::Exception::MissingWayOfFindingObjectException: invalid attribute: :css. Which kinda makes things a little less readable and/or useful if you ask me, but might be a quick and easy solution worth the slight cost in that regard.
Or you can use .driver or .wd methods to access webdriver functionality when you need it for something not supported by watir. (but that's also not quite as readable)
I have been using GQuery for a while now.
I recently came across some unexpected behaviour.
I have 2 different scenarios:
a) I want to select an element say
Link
I used
GQuery.$("a[href*=site:helloworld.com]")
This doesn't seem to work.
b) The other thing I wish to select is
<h1 data-title="SampleTitle">Title</h1>
I need to extract the value of data-title. I used the following:
GQuery.$("h1").attr("data-title");
Now, this doesn't return an error but it doesn't return the value either. It just returns a blank String. This doesn't have anything to do with the '-', is it?
What am I doing wrong here?
a) I did a quick test, and the selector works fine! (But you have to quote the value as mentioned in another answer: GQuery.$("a[href*=\"site:helloworld.com\"]"))
Note: The "*=" selector is defined in CSS 3. Gwtquery usually only supports CSS 2 at the moment, see http://code.google.com/p/gwtquery/wiki/CssGuide#CSS_scope
b) GQuery.$("h1").attr("data-title"); works for me: It returns the full attribute string value of the first "h1" element in the document
I'm using gwtquery 1.1.0 + GWT 2.4.0. If possible, try to upgrade to these (or later) versions.
Does anyone know of an easy way, using jQuery, to select all <select> elements whose val() attribute yields a certain value?
I'm trying to do some validation logic and would like to just select all those elements with a single selector, then apply a warning class to each of their parents. This I know how to do once I select all the elements, but I didn't see a selector that handles this case.
Am I going to have to select all of the <select> elements into a selector, then iterate through them and check each of their values? I was hoping there would be a simpler way.
Thanks.
Why doesn't select[value=x] work? Well firstly because <select> doesn't actually have a value attribute. There is not a single value of a select box: there may be no selected options (there shouldn't normally be, but there can be in at least IE), and, in a <select multiple>, there can be any number of selected options.
Even input[value=x] doesn't work, even though <input> does have a value attribute. Well, it does work, it just doesn't do what you think. It fetches the value of the value="..." attribute in the HTML, not the current value you have entered into the form. The value="..." attribute actually corresponds to the defaultValue property and not value.
Similarly, option[value=x][selected] doesn't work because it is checking the <option selected> attribute from the HTML source (selected attribute -> defaultSelected property) and not the current selectedness of the option (selected property not attribute) - which might have changed since the page was loaded.
Except in IE, which gets the value, selected etc form attributes wrong.
Except (again): Tesserex's example may seem to work, and the reason for that is that that it's using a non-standard jQuery-specific selector, :has. This causes the native querySelectorAll methods of modern browsers to fail, and consequently jQuery falls back to its own (native JavaScript, slow) selector engine instead. This selector engine has a bug where it confuses properties for attributes, allowing [value=x] to do what you expected, and not fail like it should! (Update: this is probably no longer the case in newer jQuery versions.)
Summary: form field state checking and selectors don't mix. Apart from these issues, you also have to worry about escaping issues - for example, what if the value you want to test against contains quotes or square brackets?
So instead, yes, you should check it manually. For example using a filter:
$('select').filter(function() {
return $(this).val()==='the target value';
}).parent().addClass('warning');
(There is a value property in HTML5 and supported by modern browsers, that when you read it gives you the value of the first selected <option>. jQuery's val() is safe to use here because it provides the same method of getting the first selected option even on browsers that don't support this.)
The existing answers don't work on select tags, but I found something that does. Ask for a select that has a selected option.
$("select:has(option[value=blah]:selected)")
You can use :
$("select[value=X]");
where X is the value against which you want to check the select's value.
Attribute selectors Is what you're looking for I believe.
Something like $+('element[attribute="value"]')
See also:
*= anywhere
^= starts with
$= ends with
~= contains word
etc.
You can create a change event that puts the value in a custom attribute on the select element whenever the value changes. You can then use a simple selector to find all of the select elements that have that value. For example:
$("select").on("change", function (e) {
var $select = $(e.currentTarget);
$select.attr("select-value", $select.val());
});
And then you can do this:
var $matches = $("select[select-value='" + searchVal + "']");
$matches will have all of your matching selects.
This is a lot easier than having to iterate through elements. Remember to set select-value to the initial value when rendering the page so you don't need to trigger a change event for each select so the select-value is set.
print $tree->findvalue('//a[1]');
I am using HTML::TreeBuilder::XPath in perl. Now i expect the above statment to return the value of second "a" element but instead it returns the value of all "a" elements in the page. I cant understand Why?
what you have shall return first a-child of every element.
so //a[1] will work as follows (result will be 2 nodes):
X
Y
a <-- give you this
a
Z
a <-- and this
a
try (//a)[1] instead
That XPATH expression looks for all a elements at every level of the document and the predicate filter selects the first a at every step.
So, depending on how your XML is structured, you might not get every a element (if there were more than one a that were siblings, you would only get the first one of those siblings).
However, if you intended to just select the first a in the document, you could use this expression: (//a)[1]
Wrapping the selection of //a in the parenthesis creates a collection that the predicate filter is then applied, selecting the first in the collection, rather than the first a encountered at each step of the //.
$(this+"p").slideDown("slow");
$(this)+$("p").slideDown("slow");
$("this+p").slideDown("slow");
does not work.
Yeah, your syntax is bad. You should use the jQuery Sibling function:
$(this).siblings().find("p").slideDown("slow");
The jQuery API site is awesome for looking stuff like this up, I rely on it nearly daily. I'd keep an eye on it.
Next.
$(this).next("p").slideDown("slow")
Make sure that the "p" element is directly adjacent, though. Otherwise you'll want to use nextAll.
jQuery have not seemed to apply this? Possibly the syntax we are trying to use is incorrect.
next() can only select elements with an ID or Class - Not just a naked dom element as expected.
Instead use. > means select first level decends only.
$('body > div').hide();
But this gives the exact same result
$('body').children('div').hide();
But,
Next
$('body + div').hide();
and
Previous
$('body ~ div').hide();
Do not seem to work as expected? But jQuery use it as example for CSS selection...
Possibly there is a complex syntax to achieve this but I could not figure it out...