I'm trying to parse an existing document and modify it by wrapping a div around some existing form elements.
HTML form looks a bit like this:
<form>
<label for="username">Username:</label>
<input name="username" type="text" />
<label for="password">Password:</label>
<input name="password" type="password" />
</form>
I can parse the document OK with Nokogiri and i'm aware of the wrap method but i'm struggling to grasp how to select both the label and input tags in one go and then wrap a div around these. So the result I am looking for is:
<form>
<div class="form-group">
<label for="username">Username:</label>
<input name="username" type="text" />
</div>
<div class="form-group">
<label for="password">Password:</label>
<input name="password" type="password" />
</div>
</form>
I have tried various XPaths / CSS selectors and can create a nodeset of just labels/inputs or all of the elements of the whole form. Is there any way to achieve this modification?
A single XPath expression can only return a single collection of nodes, so in order to achieve what you want you will need to make several queries, one for each label – input pair.
You can select an individual pair with something like this, assuming the markup is well behaved (i.e each input has a label before it):
//label[1] | //label[1]/following-sibling::input[1]
This will select the first label and the following input. However you want to select all such pairs. One way would be to first select all the label nodes, and then for each label select it and the following input.
labels = doc.xpath("//form/label")
labels.each do |l|
nodes = l.xpath(". | ./following-sibling::input[1]")
# nodes now contains a label-input pair...
end
I don’t think the wrap method will work to add a div element as an ancestor to each pair, as it will add the element to each member of the nodeset. You will probably have to move them manually, something like
labels = doc.xpath("//form/label")
labels.each do |l|
# Select this node and its neighbour.
nodes = l.xpath(". | ./following-sibling::input[1]")
# Create the new element, and add it before the label.
div = Nokogiri::XML::Node.new('div', l.document)
l.before(div)
# Move each of the pair onto this new element.
nodes.each do |n|
div.add_child(n)
end
end
Note that this method doesn’t move any text nodes, so you may find the whitespace of your document changes a bit.
Related
I have the following structure (it's just for sample). In protractor, I am getting the top element by id. However, the other elements do not have id's. I need to get the "label" element that contains the text '20'. Is there an easy way in protractor to select the element with a specific tag that contains a specific text from all the descendants of a parent element?
<pc-selector _... id="Number1">
<div ...></div>
<div ...>
<div ...>
<check-box _...>
<div _ngcontent-c25="" ...>
<label _ngcontent-c25="">
<input _ngcontent-c25="" type="checkbox">
<span _ngcontent-c25="" class="m-checkbox__marker"></span>
20 More text to follow</label>
</div>
</check-box>
</div>
</div>
</pc-selector>
I could't find anythitng, so I have tried with xpath, but protractor complains that my xpath is invalid:
parentElement = element(by.id('Number1'));
return parentElement.element(by.xpath(".//label[contains(text(),'20'))]"));
Any ideas?
You have an additional bracket in your [contains(text(),'20'))] which is likely causing you issue but there are multiple other ways this can be achieved using a single XPath or chaining other locators.
The process is that you must find the div with the correct id first and then locate the label that is a child of it.
//Xpath
element(by.xpath("//pc-selector[#id='Number1']//label[contains(text(),'20')]"));
//Chained CSS
element(by.id('Number1')).element(by.cssContainingText('label','20'));
You also may be interested to learn about xpath axes which can allow us to do very dynamic selection.
You can use the direct xpath to access the label.
element(by.xpath("//*[#id='Number1']//label"));
With the help of this SO question I have an almost working xpath:
//div[contains(#class, 'measure-tab') and contains(., 'someText')]
However this gets two divs: in one it's the child td that has someText, the other it's child span.
How do I narrow it down to the one with the span?
<div class="measure-tab">
<!-- table html omitted -->
<td> someText</td>
</div>
<div class="measure-tab"> <-- I want to select this div (and use contains #class)
<div>
<span> someText</span> <-- that contains a deeply nested span with this text
</div>
</div>
To find a div of a certain class that contains a span at any depth containing certain text, try:
//div[contains(#class, 'measure-tab') and contains(.//span, 'someText')]
That said, this solution looks extremely fragile. If the table happens to contain a span with the text you're looking for, the div containing the table will be matched, too. I'd suggest to find a more robust way of filtering the elements. For example by using IDs or top-level document structure.
You can use ancestor. I find that this is easier to read because the element you are actually selecting is at the end of the path.
//span[contains(text(),'someText')]/ancestor::div[contains(#class, 'measure-tab')]
You could use the xpath :
//div[#class="measure-tab" and .//span[contains(., "someText")]]
Input :
<root>
<div class="measure-tab">
<td> someText</td>
</div>
<div class="measure-tab">
<div>
<div2>
<span>someText2</span>
</div2>
</div>
</div>
</root>
Output :
Element='<div class="measure-tab">
<div>
<div2>
<span>someText2</span>
</div2>
</div>
</div>'
You can change your second condition to check only the span element:
...and contains(div/span, 'someText')]
If the span isn't always inside another div you can also use
...and contains(.//span, 'someText')]
This searches for the span anywhere inside the div.
I have used pattern I want to price equal 30 or Greater BUT not less.
Look my html code -
<input type="text" required="required" pattern="29+\.[0-9]*[1-9][0-9]*$" data-error="#Please enter price equal 30 or more" placeholder="Price" id="price" class="form-control" autocomplete="off" name="price" />
<div class="help-block with-errors"></div>
Above co I have used pattern="29+\.[0-9]*[1-9][0-9]*$" but this pattern not working. I have tried in different expression like 29*\.[0-9]*[1-9][0-9]*$, ^\d{30,}$, ^[0-9]\{30,}\$ these expressions also not working.
I am using bootstrap validator. Link = http://1000hz.github.io/bootstrap-validator/#validator-examples
Please help me.
*Edit : *
Now I am using ^[3-9]+\d*$ this is working fine. But it takes 3 or more than 3. I need 30 or more
You really should not be using regexes for this kind of task. But if you do want to, try this:
([3-9][0-9]|[0-9]{3,})
Suppose I have this (simplified):
<form id="myform">
<!-- some input fields -->
<input type="submit" value="proceed"/>
</form>
Then I can select the submit button by XPath //form[#id='myform']/input[#type='submit']. Great.
However, my templates might change and I want to be flexible in the depth in which the submit button is located. It might be put in a table, like this:
<form id="myform">
<!-- some input fields -->
<table><tr><td>
<input type="submit" value="proceed"/>
</td></tr></table>
</form>
I know I can select elements which are grandchildren, but I can't select grand-grand-grand-...-childeren of any depth. E.g.:
//form[#id='myform']/*/input[#type='submit'] only selects grand-children, no further depths.
//form[#id='myform']/*/*/input[#type='submit'] only selects grand-grand-children, no further or less depths.
//form[#id='myform']/**/input[#type='submit'] is not valid.
So, how do I select this submit button reliably without using element IDs?
You're almost there. Simply use:
//form[#id='myform']//input[#type='submit']
The // shortcut can also be used inside an expression.
If you are using the XmlDocument and XmlNode.
Say:
XmlNode f = root.SelectSingleNode("//form[#id='myform']");
Use:
XmlNode s = f.SelectSingleNode(".//input[#type='submit']");
It depends on the tool that you use. But .// will select any child, any depth from a reference node.
//form/descendant::input[#type='submit']
I am currently using oracle APEX 4.1 and have an issue with creating radiogroups dynamically.
I have a simple requirement(!) to display a list of questions from a table and display a yes/No radiogroup button next to each question. The list of questions may vary, so is not static.
To do this, I decided to create a plsql anonymous block and a sample of the code is below:
For c1 IN (select * from question)
LOOP
v_row:=v_row+1;
v_rowName:='F'||v_row;
v_radioYes:='<input type="radio" name='||v_rowName||' value="yes" />Yes';
v_radioNo:='<input type="radio" name='||v_rowName||' value="no" />No';
v_radio:=v_radioYes||' '||v_radioNo;
htp.p('<tr><td>'||v_row||'. '||c1.Question_text||'</td><td>'||v_type||'</tr>');
END LOOP;
So the questions are being displayed and the radiogroups are also being displayed next to each question.
My issue is that on submit, I need to find out what options a user has selected for each question and save to a database. Easy, but I cannot reference the radiobuttons for each question to find out what a user has checked.
Ideally, these radio button should have been created using the APEX tool, but I could not get this to do in a loop dynamically. Is there a way of referencing these radiogroups that have been created dynamically? Am I taking the wrong approach?
I have encountered almost the same problem recently. My solution to find the selected option was to add p_onchange parameter to APEX_ITEM.RADIOGROUP function invocation (refer to Apex 4.1 API Refernce). You can place any javascript code there. I have written a simple function to assign selected value to a hidden input field.
Here is the js function:
function put_selected_value(sel_value) {
$x('P66_SELECTED_VALUE').value=sel_value;
}
and RADIOGROUP
apex_item.RADIOGROUP(10, wrk_id, null, null, null, null,
'javascript:put_selected_value(this.value);')
Pfew. At first i thought this'd be a bit easier, but after mucking around a while i found radiogroups to be quite the toothgrinder. (this is on apex 4.1 btw)
Normally i'd respond that using apex_item would be the way to go to dynamically create items. However, radiogroups react with a twist.
If you'd use for example:
for r in(select level l, 'question '||level q, 'Y' a
from dual
connect by level < 6)
loop
htp.p('<div>'||r.q);
htp.p(
APEX_ITEM.TEXT(
p_idx => 1,
p_value => r.a,
p_size => 3,
p_maxlength => 1)
);
htp.p('</div>');
end loop;
The output would be:
<div>question 1
<input type="text" name="f01" size="3" maxlength="1" value="Y" />
</div>
<div>question 2
<input type="text" name="f01" size="3" maxlength="1" value="Y" />
</div>
<div>question 3
<input type="text" name="f01" size="3" maxlength="1" value="Y" />
</div>
<div>question 4
<input type="text" name="f01" size="3" maxlength="1" value="Y" />
</div>
<div>question 5
<input type="text" name="f01" size="3" maxlength="1" value="Y" />
</div>
Great! 5 fields generated, accessible by using apex_application.g_F01 array, where field 1 would be apex_application.g_f01(1).
Radiogroups however react differenty. What APEX_ITEM.RADIOGROUP does is not actually creating a radiogroup, but creating a radiobutton. The group would then be the f01 array.
Woah! I don't like that! The F## arrays only go from 1 to 50 to start with, so if you'd generate a few items, this is not ideal.
Also, your logic gets screwed: suddenly you don't have to loop over an array anymore, but over arrays.
For example, the output would be like this if you'd replace apex_item.text with apex_item.radiogroup
<div>question 1
<input type="radio" name="f01" value="Y" />Yes?
</div>
<div>question 2
<input type="radio" name="f01" value="Y" />Yes?
</div>
<div>question 3
<input type="radio" name="f01" value="Y" />Yes?
</div>
<div>question 4
<input type="radio" name="f01" value="Y" />Yes?
</div>
<div>question 5
<input
It sort of did what you asked: it created a radiogroup consisting of the items. If you'd click the button at question 4 and question 1 was previously marked, the mark will be removed from question 1 and set to 4.
It works however. When you submit and loop over the F01 array, it'll pass the value along:
--this after submit process
for i in 1..apex_application.g_f01.count
loop
apex_debug_message.log_message('array F01: item '||i||': '||apex_application.g_f01(i));
end loop;
--results in this debug message:
--array F01: item 1: Y
Okay, so what are your options then? Don't use this. It is not dynamic! You can't loop over the 50 arrays, unless you write 50 loops over each array...
So how does APEX actually work then? When looking at the output code of a page item radiogroup, this is the output:
<input type="hidden" name="p_arg_names" value="50795996117686343389" />
<fieldset id="P3_RGROUP_PAGE_ITEM" tabindex="-1" class="radio_group">
<input type="radio" id="P3_RGROUP_PAGE_ITEM_0" name="p_t01" value="Yes" checked="checked" />
<label for="P3_RGROUP_PAGE_ITEM_0">Y</label><br />
<input type="radio" id="P3_RGROUP_PAGE_ITEM_1" name="p_t01" value="No" />
<label for="P3_RGROUP_PAGE_ITEM_1">N</label></fieldset>
See the hidden item? I assume apex puts the selected value in that hidden item, so you can easily reference the chosen value of the radiogroup.
This is probably the best way you can solve this. Generate your radiogroups and buttons and put the selected value in a hidden item using javascript. You can create the hidden item using apex_item.hidden, which you can thankfully refer to easily in an array afterwards. If you'd use p_idx => 1, all hidden items will be stored to array g_f01.
You'll just need to piggyback this hidden item in your radiogroup container. Then bind an onchange event to your radiobuttons.
Example, look at this jsfiddle. This'll keep the selected value in the hidden item, which will be posted on submit.
Sorry for the lenghty post, i didn't think it'd get so complicated. Hopefully someone comes along and shows us an easy way :)