I am trying to write some JavaScript to help fill out forms repeatedly (personal use code). JS runs in Chrome debug console. For example
addressSection = document.querySelector("[data-automation-id='addressSection']");
addressSection.querySelector("[data-automation-id='addressSection_addressLine1']").value='1600 Pennsylvania Avenue';
addressSection.querySelector("[data-automation-id='addressSection_city']").value='Washington';
addressSection.querySelector("[data-automation-id='addressSection_countryRegion']").textContent='DC';
It works well, except there's one particular element that I can't handle. It looks like a dropdown
I looked at this solution, but I don't really see a "select" element in the html (below).
<div data-automation-id="sourceSection">
<div data-automation-id="formField-sourceDropdown" class="css-7t35fz">
<label for="input-129" required="" class="css-1rncms5">How Did You Hear About Us?<abbr title="required" class="css-1fc83zd">*</abbr></label>
<div class="css-15rz5ap">
<div style="width: 100%; max-width: 344px; min-width: 280px;">
<div class="css-12zup1l">
<button aria-haspopup="listbox" type="button" value="fe2473ba8eaa1001fdddfb8cc4d00002" data-automation-id="sourceDropdown" id="input-129" aria-label="How Did You Hear About Us? HBCUConnect required" class="css-6l1l38">HBCUConnect</button>
<input type="text" class="css-77hcv" value="fe2473ba8eaa1001fdddfb8cc4d00002">
<span class="menu-icon css-gvnnq4"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="wd-icon-caret-down-small wd-icon" focusable="false" role="presentation" viewBox="0 0 24 24"><g fill-rule="evenodd" class="wd-icon-container"><path d="M12.288 15.866c.117.18.31.177.424 0l4.235-6.538c.116-.18.034-.328-.176-.328H8.229c-.214 0-.29.15-.176.328l4.235 6.538z" class="wd-icon-fill"></path></g></svg>
</span>
</div>
</div>
</div>
</div>
</div>
I tried:
document.querySelector("[data-automation-id='sourceDropdown']").value='LinkedIn';
That didn't do anything. Based on Louys suggestion in the comments, I tried
myElement.dispatchEvent(new Event('change', {'bubbles': true}));
afterwards. Didn't seem to do anything. Then I tried
document.querySelector("[data-automation-id='sourceDropdown']").textContent='LinkedIn';
That changed the displayed value that I see, but the server doesn't really capture it.
Any thoughts?
Related
Hey all I am having the worst time trying to figure out why my XPath code below is not able to find the Image tag and the HREF link that goes with it within my document.
The XPath (full) looks like this:
//html/body/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/a/div/svg/g/descendant::image[starts-with(#href,'https://')]
The javascript code I am using is:
function checking(Path) {
const nodes = document.evaluate(Path, document, null, XPathResult.ANY_TYPE, null);
const result = {
Data: []
};
let attr = nodes.iterateNext();
result.Data.push({ href: attr});
return JSON.stringify(result);
}
console.log(checking("//html/body/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/div/a/div/svg/g/descendant::image[starts-with(#href,'https://')]"));
And the HTML that I am looking through to get said image Xlink:HREF:
<body class="">
<div id="" style="">
<div>
<div class="">
<div class="">
<div class="">
<div class="">
<div class="">
<div class="">
<div class="">
<div class="">
<div class="" role="5ma">
<div class="">
<div class="">
<div class="">
<div>
<div class="">
<div class="">
<div class="">
<a aria-label="" class="" href="https://www.this.com/link/is/not/needed" tabindex="0">
<div class="">
<svg aria-label="" class="" data-visualcompletion="ignore-dynamic" role="img" style="height: 168px; width: 168px;">
<g mask="url(#)">
<image x="0" y="0" height="100%" width="100%" xlink:href="https://www.google.com/logos/doodles/2021/seasonal-holidays-2021-6753651837109324-6752733080595603-cst.gif" style="height: 168px; width: 168px;"></image>
<circle class="" cx="8" cy="4" r="4"></circle>
</g>
</svg>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
I keep getting NULL for the output for some reason? And here is a jsfiddle to visually test it.
{
"Data": [
{
"href": null
}
]
}
Anyone have a clue as to why I am?
UPDATE 1
Checking the "offical" xPath to my project comes out to be this:
// html/body/div1/div/div1/div/div3/div/div/div1/div1/div/div/div1/div2/div/div/div/div1/div/div/svg/g/image
Which I change the latest fiddle to reflect what #bigless suggested in his fiddle but still get null.
Newest fiddle
Alternative to #Jack Fleeting answer (skipping all those divs just for example) with xlink:href selector:
string(//*[name() = 'svg']/*[name()='g']//*[name()='image' and starts-with(#*[name()='xlink:href'],'https://')]/#*[name()='xlink:href'])
This will extract just attribute value as a string (first occurence)
A few things:
First, you have a problem with namespaces and Deprecated XLink URL reference attributes.
Second, in
result.Data.push({
href: attr
});
you should push the node value of the attribute:
result.Data.push({
href: attr.nodeValue
});
Finally, because of the namespace issue, and to simplify the xpath expression, change your comeback to
var comeback = checking("//*[local-name()='image'][starts-with(./#href,'https://')]/#href");
And it should work as in this fiddle.
I have some HTML that should respond to user interaction. It is a page that I am trying to get to work offline. After I load the page while online I end up with fully rendered HTML. Some of the HTML contains show/hide functionality, and is meant to prevent deafult action of an element click. For instance, see this:
<div id="area49253971_6938" class="component interaction-component float-none clear-none interaction_booted">
<div role="status" class="int-prep hidden"><i aria-hidden="true" class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i><br> Loading interaction...</div>
<div>
<div class="data-field interaction-type">RevealContent</div>
<div class="interaction_title"></div>
<div class="interaction_content RevealContent" style="min-height: 400px; width: 400px;">
<div class="pointer">
<td>
<div id="area49253971_6940" class="component image-component float-left clear-none booted"><img src="somelink" height="50" width="30" title="" alt="" style="padding-right: 10px;"></div>
<p class="body" id="area49253971_6941"><em><strong>Some question</strong></em></p>
</td>
</div>
<div style="display: block;">
<td>
<p class="body" id="area49253971_6942"><strong>Some answer</strong></p>
</td>
</div>
</div>
<div class="interaction_data" style="display: none;">
<div id="area49253971_6939" class="component table-component float-none clear-none ">
<div class="component_caption"></div>
</div>
</div>
</div>
</div>
The app's code is not working offline (I think due to app's ajax calls and a bunch of other dependencies and app-specific stuff). (By the way, you can see the app code I am trying to rewrite into pure jQuery here: https://jsfiddle.net/0s6xdk9q/1/). So I decided to rewrite the app functionality like so:
$(document).ready(function() {
$(this).on('click', '.interaction_booted', function () {
interactionData = $(this).find(".interaction_data");
this.container = $(this).find(".interaction_content");
var contentToReveal = ($(this).find('.RevealContent')).children()[1];
var initialContent = ($(this).find('.RevealContent')).children()[0];
$(contentToReveal).slideToggle('slow');
var a = initialContent.find('a');
a[0].addEventListener("click", function(e){
e.preventDefault();
}, false);
});
});
Something is not quite right. Maybe because I have multiple click events. In any case, I confirmed by stepping through the code in Chrome that the variable a is indeed the element I expect. All element selectors are working fine. But nothing I do is resulting in this preventDefault from working. I have a feeling there is a simple answer, just not seeing it. Also, I am trying to do this in a way that doesn't require me to add anything to the HTML.
Thoughts?
thanks,
Brian
I found many solutions related to my problem but no seems to solve my problem.
Let me first clear my question properly.I have many items on a page;product items that can be shown in this page.
So for every item on the page , i have an image source which is obtained from the other page.
<img itemprop="image" src="/imageThumbnail.php?product_code={$obj->product_code}&page=productDisplay" >
This is just one image tag,the whole code that displays a product looks like this
$imgID=0;
while ($obj = $results->fetch_object()) {
$productPrice=($obj->price);
$weight=$obj->amount;
$weight=str_replace('kg',"",$weight);
$products_item .= <<<EOT
<li class="product">
<form itemscope itemtype="https://schema.org/Product" class="get_method_product_forms" action="/landing_cake/" method="get">
<div style="position: relative" class="product-thumb">
<div class="box">
<img src="/cart/images/loader.gif" id="{$imgID}">
<img itemprop="image" class="loaderGIF" src="/imageThumbnail.php?product_code={$obj->product_code}&page=productDisplay" ><img id="overlay" src="/cart/images/soldOut.png"><div class="overbox">
<div class="title overtext"></div>
<div class="tagline overtext"><p style="font-size:20px;">
<span id="fon"></span><button id="{$obj->product_code}" class="wishlist" >♥</button>
</div>
</div>
</div></div>
<div class="product-content"><h3 itemprop="name" style="margin-top:10px">{$obj->product_name}</h3>
<div class="product-info">
<span itemprop="offers" itemscope itemtype="https://schema.org/Offer" >
<i itemprop="priceCurrency" content="INR" class="fa fa-inr"><span itemprop="price">{$productPrice}</span></i>
<span itemprop="weight">{$weight}</span>
<input itemprop="availability" itemtype="https://schema.org/OutOfStock" content="Out Of Stock" type="hidden" name="product_qty" value="Sold Out"/>
</span>
<fieldset>
<label content="{$obj->product_company}" itemprop="manufacturer">
{$obj->product_company}
</label>
</fieldset>
<input itemprop="productID" content="{$obj->product_code}" type="hidden" name="product_code" value="{$obj->product_code}" />
<div align="center"><button id="{$obj->product_code}" style="background:#666666" value="{$obj->product_code}" type="submit" class="SoldOut" >Add to Cart</div>
</div>
</div>
</form>
</li>
EOT;
$imgID++;
}
echo $products_item;
Now based on the solutions, i added an image tag just before the actual image tag.I tried doing something like this
1. $('.loaderGIF').on('load',function(e){//something})
//This didn't even run
2. $('img').on('load',function(e){//something})
//I can't seem to limit this to the main image.
How do i achieve my solution, i have been trying it out for a couple of hours.
Please, let me know if you need somthing more.I will update my question.Thanks!
I couldn't find my own fix, but I found this one right here on stackoverflow and it seems good to me.
If the src is already set, then the event is firing in the cached
case, before you even get the event handler bound. To fix this, you
can loop through checking and triggering the event based off
.complete, like this:
Copy from the source in case it changes:
// ".one()" makes it work only once. Change to "on" if you need it to happen more.
$("img").one("load", function() {
// do stuff
}).each(function() {
if(this.complete) $(this).load();
});
I'm in process of developing simple extension for own use. It main goal is dynamically fulling forms based on pre-defined options.
When I'm trying to update the select element from my content script it doesn't really change selected option. Selection box itself is wrapped in nice ui using collection of spans:
<!-- Data with AngularJs bindings -->
<div class="ddOutOfVision" id="drpType_msddHolder" style="height: 0px; overflow: hidden; position: absolute;">
<select id="drpType" name="Type" class="mobile100 ng-pristine ng-invalid ng-invalid-required" ng-model="Order.ccToAdd.Type" ng-change="change()" required="" tabindex="-1">
<option value="" selected="">Please select type</option>
<option data-image="../icons/V.png" value="001">V</option>
<option data-image="../icons/M.png" value="002">M</option>
<option data-image="../icons/A.png" value="003">A</option>
<option data-image="../icons/D.png" value="004">D</option>
</select>
</div>
<!-- UI -->
<div class="dd ddcommon borderRadiusTp" id="drpType_msdd" tabindex="0" style="width: 100%;">
<div class="ddTitle borderRadiusTp">
<span class="divider"/>
<span class="ddArrow arrowoff"/>
<span class="ddTitleText " id="drpType_title">
<span class="ddlabel">Please select type</span>
<span class="description" style="display: none;"/>
</span>
</div>
<input id="drpType_titleText" type="text" autocomplete="off" class="text shadow borderRadius" style="display: none;" />
<div class="ddChild ddchild_ border shadow" id="drpType_child" style="z-index: 9999; position: absolute; visibility: visible; height: 168px; top: 33px; display: block;">
<ul>
<li class="enabled _msddli_ selected">
<span class="ddlabel">Please select type</span>
<div class="clear"/>
</li>
<li class="enabled _msddli_">
<img src="../icons/V.png" class="fnone" />
<span class="ddlabel">V</span>
<div class="clear"/>
</li>
<li class="enabled _msddli_">
<img src="../icons/M.png" class="fnone" />
<span class="ddlabel">M</span>
<div class="clear"/>
</li>
<li class="enabled _msddli_">
<img src="../icons/A.png" class="fnone" />
<span class="ddlabel">A</span>
<div class="clear"/>
</li>
<li class="enabled _msddli_">
<img src="../icons/D.png" class="fnone" />
<span class="ddlabel">D</span>
<div class="clear"/>
</li>
</ul>
</div>
</div>
What I've try (and it doesn't actually change selected element):
jQuery
$("#drpType").val("001");
$("#drpType").val("001").change();
$("#drpType").val("001").trigger('input');
$("#drpType").val("001").trigger('input').change()
Native javascript:
document.forms[0].elements[0].selectedIndex = 1;
document.forms[0].elements[0].options[2].selected = true
Modern DOM selector:
document.querySelector('#drpType [value="001"]').selected = true
And even click() instead of select \ value...
Additional notes:
content script loaded at "run_at": "document_end"
site is build using angularjs and those elements created dynamically after document is ready, so for testing purposes I've
just implemented a wait to be sure that elements are already present
in DOM. It's dirty hack, but this is not a object for this
discussion.
setTimeout(function () { /* my code here */ }, 8000);
UPDATE
I finally get changed the value of underlying data option and connected UI option by jQuery, and only in Chrome developer mode, by code below:
$("#drpType option[value='001']").prop("selected", true).trigger('input').change()
But this works only from developer console and not from context script. Does someone know why?
UPDATE
Meanwhile above solution for changing value works for both Chrome console and Tampermonkey user script, it doesn't works for my extension...
Finally I've managed to solve this task. In content script:
document.querySelector("#drpType option[value='001']").selected = true;
document.querySelector("#drpType").dispatchEvent(new Event('change', {bubbles: true}));
Details:
Because context script is running in isolated environment we need to manually trigger events, so external web-page could also trigger all subscribers. jQuery update and trigger calls for some reason do not trigger global DOM event's, and for this case I have to manually create and trigger update event.
More complex approach for event was mentioned in this answer: https://stackoverflow.com/a/2856602/1187217
If you need it to simulate the real event in full, or if you set the
event via the html attribute or addEventListener/attachEvent, you need
to do a bit of feature detection to correctly fire the event:
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", true, true);
element.dispatchEvent(evt);
}
else
element.fireEvent("onchange");
Here's my code below.
<%#allemployeeleaveapplications.each do |each_employee_leave_application|%>
<div class="table_row 1" onclick="showHide('divHidden1');" style="cursor:pointer;">
<div class="left_expand_collapse" style="margin-right:6px;">
+
</div>
<div class="table_column_no_margin" style="width: 10%;">
<%=each_employee_leave_application.staffname%>
</div>
<div class="table_column" style="width: 10%;">
<%=each_employee_leave_application.leavetypedescription%>
</div>
<div class="table_column" style="width: 10%;">
<%=each_employee_leave_application.totaldaystaken%> Days
</div>
<div class="table_column" style="width: 10%;">
<%=calculateRemainingLeaveEntitlements(each_employee_leave_application)%> Days
</div>
<div class="table_column" style="width: 10%;">
<%=number_to_currency(each_employee_leave_application.totaldaystaken * 7.5 * each_employee_leave_application.calculatedratesamount, :precision=>2)%>
</div>
<div class="table_column" style="width: 100px;">
<%=each_employee_leave_application.startdate%>
</div>
<div class="table_column" style="width: 46px;">
<%=each_employee_leave_application.enddate%>
</div>
<% if each_employee_leave_application.applicationstatusid ==3 %>
<div class="table_column" style="width: 184px; display:inline-flex;margin-left:40px;">
<div style="float:left;">
<%= button_to 'Approve', { :action=>"update_leave_entitlement_status",
:leave_entitlement_type_id=>each_employee_leave_application.leave_entitlement_type_id,
:employee_id=>each_employee_leave_application.employee_id, :status_id=>"1"},style: 'background-color: #6EBA66;width: 88px; height:22px; margin-right: 4px; padding-top:3px;padding-bottom:22px; cursor:pointer',
method: :post, data: { confirm: 'Are you sure you want to approve this staff leave application?' } %>
</div>
<div style="float:left;">
<input type="button" name="Reject" value="Reject" style="background-color: #D70D1E;width: 88px; height:22px;padding-top:3px; padding-bottom:22px;">
</div>
</div>
<% else %>
<div class="table_column" style="width: 50px;">
<%=each_employee_leave_application.applicationstatusname%>
</div>
<% end %>
</div>
<% end %>
Basically what I'm saying is that I have an array of all employee leave applications and I'm looping each of the items inside employeeleaveapplications and render its individual field data such as their entiltlement type, username, start date of leave application, end date of leave application etc. And I placed a button tag to create a submit button for the manager to approve/reject these staff leave entitlements.
Whilst the code logic is fine, I encountered some bizzare behaviours when viewsing my app under Google Chrome. This is what I found for a staff leave entitlement that's not yet approved or rejected.
<div style="float:left;">
<div>
<input data-confirm="Are you sure you want to approve this staff leave application?"
style="background-color: #6EBA66;width: 88px; height:22px; margin-right: 4px; padding-top:3px;padding-bottom:22px; cursor:pointer"
type="submit" value="Approve">
<input name="authenticity_token" type="hidden" value="fN4Vb/PvtALrAE7TtzAb3Hp/cJEpbkvF22sE1Pttt6I=">
</div>
</div>
This is totally incorrect when I'm expecting to see the form tag above the input tag comment, along with the details of my custom controller/action/parameters in one line. But it's not rendering that tag as I originally wanted it.
How can a simple code setup could suddenly become totally senseless when it's not rendering the form tag fields as it should be? I mean the form tag fields do appear in the browser at times - but not all the times! It ended up in this state! I dont' really understand why that's the case?
Can someone out there shed some light on this and tell me where did I, possibly, do wrong with my code.
Much appreciated.
Never mind. I found out where the problem lies.
It was because I had another extra tag lying around in my html code somewhere, which interferes the rendering for the buttom forms above!
All sorted now.