0

I have a Calendar table which shows both enabled and disabled dates. I want to be be able to select day 30 from the table which is not disabled. How do I select just this element to click on?

<table class="calendar">
    <tbody>
        <!-- ko foreach: calendarRows.weeks -->
        <tr class="calendar-week" data-bind="foreach: days">
            
            <td data-bind="css: { disabled: disabled, selected: selected }, click: select, event: {keypress: select}" class="calendar-day disabled">
                <span class="day-number" data-bind="text: dayNumber" tabindex="0">30</span>
            </td>
        </tr>
        
        <tr class="calendar-week" data-bind="foreach: days">
        
            <td data-bind="css: { disabled: disabled, selected: selected }, click: select, event: {keypress: select}" class="calendar-day">
                <span class="day-number" data-bind="text: dayNumber" tabindex="0">30</span>
            </td>
        </tr>
        <!-- /ko -->
    </tbody>
</table>

Original XPATH which failed when two 30's were displayed was:

//span[contains(@class,'day-number')][(text()='" + day + "')]

Tried:

.//td[(@class='calendar-day') and not(@class='disabled')]/span[contains(@class,'day-number')][(text()='" + day + "')]

But no luck.

What am I doing wrong? Assistance greatly appreciated.

Robert
  • 11
  • 2

2 Answers2

1

Thanks for the input. The answer to this problem was:-

.//td[not(contains(@class,'disabled'))]/span[contains(@class,'day-number')][(text()='" + day + "')]

Robert
  • 11
  • 2
  • 1
    Please be aware that this works in this specific example because of the order and placement of the class attributes. If this changes such that disabled can come first or another class or classes come between them (eg “disabled calendar-day” or “calendar-day today disabled”) this will not match appropriately. This is especially if the class attribute is dynamically generated or altered by JavaScript in the browser during/after rendering. – Andy Mudrak Dec 22 '22 at 13:01
  • Surely contains(@class,'disabled') will match on 'disabled' irrespective of its position or surrounding text? Is that not the purpose of contains rather than = ? – Robert Dec 22 '22 at 15:19
  • Just realised I may have refactored that answer while you were typing your feedback. I think I have the correct answer now. – Robert Dec 22 '22 at 15:33
  • You were right that contains(@class'disabled') will always match if disabled is in the class attribute. I just was mentioning the caveats if there are other class names that could be used, or if this technique is used in other situations besides this one in the future. – Andy Mudrak Dec 22 '22 at 17:45
0

Neither td element has a class that is equal to exactly the text “disabled”, and your second xpath expression is asking for the class to not be equal to disabled. And because both do not equal “disabled” they are both returned. That is, the expression not(@class='disabled') is evaluating if the class attribute text value is not exactly equal to the text “disabled”.

In xpath, the attribute “class” is treated like any other attribute and doesn’t look for class names, just the full value of the attribute. It is not aware of the meaning of the class attribute in html and css rendering and is just trying to match the full value “calendar-day disabled” to your expression.

Thus you want to build an expression for “does not contain” the text “disabled”. The same for checking if the class contains “calendar-day”. In this specific and limited case this works, but note that “contains()” is a substring match so it may not work as intended if there could be other class names that partially contain your search string, such as “disabledPreviously”.

Here’s an example of doing a not contains (it doesn’t use the class attribute specifically but still an example of doing not contains).

https://stackoverflow.com/a/11024134/2346994

Andy Mudrak
  • 787
  • 3
  • 7
  • I posted an edit to clarify. The expression `not(@class='disabled')` is evaluating that the full text value of the class attribute is not equal to exactly “disabled”. If all that was in the first td class attribute was “disabled” and nothing else it would not return that td. But since you have two classes (and for html class attributes in general the expectation is that you can have multiple classes) you need contains or something that can check if the full text value “has” the disabled class, rather than “it has only one class and that it is not ‘disabled’”. – Andy Mudrak Dec 22 '22 at 12:39
  • Actually you would need a contains for calendar day and a not contains for disabled. – Andy Mudrak Dec 22 '22 at 12:45