adding extra td element - c#

I have this table.
<tr>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black"> Hello</font></td>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black">Bye</font></td>
</tr>
but I want to add an extra td element at the end so it looks like this:
<tr>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black"> Hello</font></td>
<td style="padding-left: 25pt;"><font style="background-color: white" color="black">Bye</font></td>
<td>1</td>
</tr>
I am using htmlAgilityPack, but the examples online that I have looked don't exactly help.

i hope this may help you
string data = #"<tr><td style='padding-left: 25pt;'><font style='background-color: white' color='black'> Hello</font></td><td style='padding-left: 25pt;'><font style='background-color: white' color='black'>Bye</font></td></tr>";
string rowToAppend = "<td>1</td>";
var html = new HtmlAgilityPack.HtmlDocument();
html.LoadHtml(data);
var table = html.DocumentNode.SelectNodes("tr").FirstOrDefault();
var node = HtmlNode.CreateNode(rowToAppend);
table.AppendChild(node);
data += table.OuterHtml;

Related

Unable to locate element inside a table using xpath with selenium

I want to click on a button inside my table each row has a update button I want to click on a specfic button inside my table.
Here is a what my table looks like:
<table _ngcontent-vhp-c82="" datatable="" id="dtOptionsComments" class="display table table-striped table-bordered dt-responsive dataTable dtr-inline" aria-describedby="dtOptionsComments_info" style="width: 100%;" width="100%">
<thead _ngcontent-vhp-c82="">
<tr _ngcontent-vhp-c82="">
<th _ngcontent-vhp-c82="" class="no-marking sorting_disabled" rowspan="1" colspan="1" style="width: 50.4px;" aria-label=""></th>
<th _ngcontent-vhp-c82="" class="sorting sorting_asc" tabindex="0" aria-controls="dtOptionsComments" rowspan="1" colspan="1" style="width: 1109.4px;" aria-label="Comment.Comment Shipping.ShippingDatatable.aria.sortDescending" aria-sort="ascending">Comentario Shipping.Shipping</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="no-marking dtr-control">
<a href="javascript:void(0);">
<span data-toggle="modal" data-target="#update-modal" update-comment-text="6 MESES DE GARANTIA" update-comment-id="5" class="material-icons md-18 clickable"> edit </span>
</a>
<a href="javascript:void(0);">
<span data-toggle="modal" data-target="#delete-modal" delete-comment-id="5" class="material-icons clickable">delete</span>
</a>
</td>
<td class="sorting_1">6 MESES DE GARANTIA</td>
</tr>
<!-- MORE ROWS!!! -->
</tbody>
<tfoot _ngcontent-vhp-c82="">
<tr _ngcontent-vhp-c82="">
<td _ngcontent-vhp-c82="" class="no-marking" rowspan="1" colspan="1">
<a _ngcontent-vhp-c82="" href="javascript:void(0);">
<span _ngcontent-vhp-c82="" class="material-icons clickable"> add_box </span>
</a>
</td>
<td _ngcontent-vhp-c82="" rowspan="1" colspan="1">
<input _ngcontent-vhp-c82="" formcontrolname="addComment" type="text" id="addComment" name="addComment" class="form-control ng-untouched ng-pristine ng-invalid">
<!---->
</td>
</tr>
</tfoot>
</table>
Here is my code trials:
IWebElement btnUpdate = _driver.FindElement(By.XPath("//*[update-comment-id='" + commentAction.GetLastQuoteInsertId().ToString() + "']"));
btnUpdate.Click();
I have validated that the function GetLastQuoteInsertId returns the proper value
Why is my xPath selector wrong how can I fix it thank you for your help.
You were almost there. While considering a xpath the attribute_name should be always preceded by a # sign.
Additionally to make the xpath more canonical as the element is a <span> element you can mention //span to start the xpath.
Effectively, your line of code will be:
IWebElement btnUpdate = _driver.FindElement(By.XPath("//span[#update-comment-id='" + commentAction.GetLastQuoteInsertId().ToString() + "']"));
btnUpdate.Click();

Avoiding StaleElementReferenceException when dealing with a dynamic table in Selenium

I am having trouble dealing with a dynamic table.
This is our table:
<table class="table" style="min-width: 870px; max-width: 870px">
<colgroup>
<col style="width: 30px">
<col style="width: 200px">
<col style="width: 80px">
<col style="width: 70px">
<col style="width: 200px">
<col style="width: 50px">
<col style="width: 50px">
</colgroup>
<tbody>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
</tbody>
<tfoot>
<tr>
<td class="text-right" colspan="3">Media del grupo</td>
<td class="text-center media" colspan="1">1</td>
<td class="text-center noeditable" colspan="1"></td>
<td class="text-center media" colspan="1"></td>
<td class="text-center media" colspan="1"></td>
</tr>
</tfoot>
</table>
Each <tr> contains the following:
<tr>
<td class="indice">1</td>
<td id="accion-1-alumno-0" data-tooltip="" class="has-tip titulo" title="">Ap_Alumno_1ESOA_1, Nb_Alumno_1ESOA_1</td>
<td data-tooltip="" class="has-tip titulo" data-selector="tooltipdtk00g" title="">1ESOA</td>
<td class="nota relative " style="text-align:center; color: #ed1c24!important">
<div id="accion-1-celda-0-0-0" class="elemento comentarios">1</div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div class="elemento comentarios"><span id="accion-1-editar-1-0" class="block left ellipsis span comentario" title=""></span><span id="accion-1-prismaticos-1-0" class="glyphicons glyph_observaciones observacion right"></span></div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div id="accion-1-celda-2-0-0" class="elemento comentarios"></div>
</td>
<td class="nota relative " style="text-align:center; color: #000000!important">
<div id="accion-1-celda-3-0-0" class="elemento comentarios"></div>
</td>
</tr>
We are interested in the elements
<div id="accion-1-celda-0-0-0" class="elemento comentarios">1</div>
which are being added to a IList<IWebElement>
However, when trying to SendKeys to the element, the first time it will work correctly, however the second time will always fail with StaleElementReferenceException, this is because the previous element (the first one) has changed and with it the page DOM has also changed.
I am trying to find a way to find the element again if StaleElementReferenceException is thrown.
So far, both this methods have failed:
Method one
public virtual void Introducir_NotasAlumnos(string nota)
{
IList<IWebElement> divNota = tablaNotas
.Select((element, index) => element.FindElement(By.Id("accion-1-celda-0-" + index + "-0")))
.ToList();
divNota.ToList().ForEach(element => Introducir_Nota(element, nota));
}
Method two
public virtual void Introducir_NotasAlumnos(string nota)
{
int index = 0;
foreach (IWebElement element in tablaNotas)
{
By locator = By.Id("accion-1-celda-0-" + index + "-0");
Introducir_Nota(element.FindElement(locator), nota);
index++;
}
}
Thanks for your time.
Here your locators:
table: .table tbody > tr
table row by index: .table tbody > tr:nth-child(1)
and you method (java code):
int size = driver.findElements(By.cssSelector(".table tbody > tr")).size();
for (int i = 0; i < size; i++) {
WebElement row = driver.findElement(By.cssSelector(".table tbody > tr:nth-child(" + i + ")"));
By locator = By.id("accion-1-celda-0-" + i + "-0");
Introducir_Nota(row.findElement(locator), nota);
}
You have certain count of rows and you find row element independently, should not throw StaleElementReferenceException exception.
Here shorter version:
int size = driver.findElements(By.cssSelector(".table tbody > tr")).size();
for (int i = 0; i < size; i++) {
Introducir_Nota(row.findElement(By.cssSelector("#accion-1-celda-0-" + i + "-0")), nota);
}

Move a button to another tr programatically

I have a login form designed using a <table> in an ASP.NET application.
A client is asking to modify the layout by moving around a few UI controls. They want to move the Forgot Password link below the Login button so they are on separate lines.
However, for all other clients, everything needs to remain the same, so if any CSS or style changes need to be done, I would need to do it programatically. Are there any easy ways to do this?
I am trying to avoid creating duplicate user controls to fit the layout they want, and then hide/show controls, depending on the client.
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
JSfiddle Demo
EDIT: jQuery NOT allowed. JavaScript OK.
Jquery can do this for you. the code might look something like this:
$("#toggle").click(toggleButtonPosition);
function toggleButtonPosition() {
var buttonRow = $("button[type=submit]").closest("tr"), buttonCell, newTR, tr;
if (buttonRow.find("td").length === 2) {
// the table is in its initial configuration. Need to extract the button cell and add it to a new row
buttonCell = buttonRow.find("button").closest("td");
buttonCell.remove();
newTR = $("<tr />").append(buttonCell);
newTR.insertBefore(buttonRow);
} else {
// Reverse the process
buttonCell = buttonRow.find("td");
tr = buttonRow.siblings().first();
tr.append(buttonCell);
buttonRow.remove();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
Toggle Button Position
You don't need to duplicate the layout. You can do it by adding/ removing the css class depending upon the client using jquery or javascript.
$(document).ready(function(){
var client1 = true; // variable to store client
if(client1){
$('button').removeClass('client2').addClass('client1');
}
else {
$('button').removeClass('client1').addClass('client2');
}});
Css classes to be added/removed from the button depending upon the client. I have put 'client1' css class on the button.
button.client1{display:block;margin:0 auto}
button.client2{float:right;margin-left:4px}
Html code as follows:
<table style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td>
<button type="submit" class='client1'>Login</button>
Forgot Password
</td>
</tr>
</table>
</td>
</tr>
You could use jQuery to archive that.
Here is an example of moving the button one step back
$(document).ready(function() {
var loginLayOut = $(".login"); // login Table
var btnTd = loginLayOut.find("button").parent(); // button container, in this case its td
var tbody = btnTd.parent().parent(); // we get the tbody by moving two step back
var tr = $("<tr></tr>"); // create a new tr
tr.append(btnTd); // append the td to the new created tr
tbody.prepend(tr); // insert the tr to the tbody at position 0
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class='login' style="TABLE-LAYOUT: fixed; WIDTH: 370px;">
<COLGROUP>
<COL width="120px">
<COL width="250px">
</COLGROUP>
<tr>
<td>Username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td> </td>
<td>
<table>
<tr>
<td style="text-align:right;">
Forgot Password
</td>
<td>
<button type="submit">Login</button>
</td>
</tr>
</table>
</td>
</tr>
</table>

how to split table rows in ASP .Net C#

I have HTML Code in a string named gridHTML
<html>
<body>
<style>a{text-decoration:none; color: black;} th { border: solid thin; }
td{text-align: center;vertical-align: middle;font-family: Arial;font-size: 8pt; height: 50px;
border-width: 1px;border-left-style: solid;border-right-style: solid;}
table { border-collapse: collapse; } tr:nth-child(1) { border: solid thin; border-width: 2px;}
tr{ border: solid thin; border-style: dashed solid dashed solid;}
</style>
<div>
<table >
<tr class='leftColumnTableHeadO' align='center' style='font-family: Arial; font-size: 8pt; font-weight: normal; width: 100px;'>
<th scope='col'>TM No.</th>
<th scope='col' style='width: 83px;'>Filing Date</th>
<th scope='col'>TradeMark</th>
<th scope='col'>Class</th>
<th scope='col'>Jr#</th>
<th scope='col'>Applicant</th>
<th scope='col'>Agent / Attorney</th>
<th scope='col'>Status</th>
<th scope='col'>City</th>
<th scope='col'>Logo</th>
</tr>
<tr class='lightGrayBg' >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl02$ctl00','')'>38255</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_0'>09-12-1962</span> </td>
<td >IMIDAN</td>
<td >5</td>
<td >158</td>
<td >test</td>
<td >test</td>
<td >Registered</td>
<td >DELWARE</td>
<td ></td>
</tr>
<tr >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl03$ctl00','')'>188389</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_1'>09-09-2003</span> </td>
<td >RAND</td>
<td >16</td>
<td >682</td>
<td >Ttest </td>
<td >test </td>
<td >Advertised</td>
<td >CALIFORNIA</td>
<td ></td>
</tr>
<tr class='lightGrayBg' >
<td ><a title='View Report' class='calBtn' href='javascript:__doPostBack('ctl00$MainContent$grdTradeMarkNumber$ctl04$ctl00','')'>207063</a> </td>
<td ><span id='MainContent_grdTradeMarkNumber_lblFilingDate_2'>11-03-2005</span> </td>
<td >FP DIESEL</td>
<td >7</td>
<td >690</td>
<td >testtest</td>
<td >testtest</td>
<td >Advertised</td>
<td >-</td>
<td ></td>
</tr>
</table>
</div>
</body>
</html>
I want to get all rows separately in a list
i am using split method to do this
List<string> rows = gridHTML.Split(new string[] { "<tr" }, StringSplitOptions.None).ToList();
but the problem is when i look into the list "<td" is removed
Is there any (other) way to get all rows in a list ?
For this one, you could use Linq To XML easily. ie:
var rows = XElement.Parse(gridHTML).Descendants("tr");
var cells = rows.Elements("td");
var cellContentsAsString = cells.Select(c => (string)c);
etc.
You should not use string methods (or regex) to parse HTML, i recommend HtmlAgilityPack:
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(gridHTML);
List<HtmlNode> trList = doc.DocumentNode.Descendants("tr").ToList();
Since it seems that you want to load this table data into a collection, maybe following approach is better for your requirement. It will load the rows and cells into a DataTable, even the DataColumns are initialized correctly with the table-header values:
DataTable table = new DataTable();
bool firstRowContainsHeader = true;
var tableRows = doc.DocumentNode.Descendants("tr");
var tableData = tableRows.Skip(firstRowContainsHeader ? 1 : 0)
.Select(row => row.Descendants("td")
.Select((cell, index) => new { row, cell, index, cell.InnerText })
.ToList());
var headerCells = tableRows.First().Descendants()
.Where(n => n.Name == "td" || n.Name == "th");
int columnIndex = 0;
foreach (HtmlNode cell in headerCells)
{
string colName = firstRowContainsHeader
? cell.InnerText
: String.Format("Column {0}", (++columnIndex).ToString());
table.Columns.Add(colName, typeof(string));
}
foreach (var rowCells in tableData)
{
DataRow row = table.Rows.Add();
for (int i = 0; i < Math.Min(rowCells.Count, table.Columns.Count); i++)
{
row.SetField(i, rowCells[i].InnerText);
}
}

How can I read the HTML inside a XML tag?

I have a XML that return, at some point, this:
<TESTO>
<img src="../path/image.jpg" alt="" />
</TESTO>
well, if I do:
string TESTO = m_oNode.SelectSingleNode("TESTO").InnerText;
TESTO will be "empty". Why? How can I read the whole text? With other tag without HTML tag all works perfectly...
I use XmlDocument
EDIT - code that create an Exception with InnerXml():
<TESTO>
<table style="width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td> </td>
<td width="700"><img src="/testata.jpg" alt="mycaf.it" width="700" height="333" border="0" /></td>
<td> </td>
</tr>
<tr>
<td> </td>
<td style="text-align: center; background-color: #f5f5f5;" align="center" bgcolor="#f5f5f5"><br />
<p style="color: #ee2e24; font-style: italic; font-size: 25px; font-family: Arial;">portale<br /> </p>
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
</TESTO>
InnerText gets only the Text (for mixed content or text content). Use InnerXml instead.
Example:
<A>
Some text in mixed content
<B>OnlyText</B>
</A
Gives the result:
InnerText = "Some text in mixed content\r\nOnlyText"
InnerXml = "Some text in mixed content\r\n<B>OnlyText</B>";
To read the content of an html element you have to use yourElement.innerXml instead of yourElement.InnerText
Per leggere il contenuto di un elemento html devi usare yourElement.innerXml al posto di yourElement.InnerText :)

Categories

Resources