javascript 在 AJAX 驱动的站点上选择和激活正确的控件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15048223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Choosing and activating the right controls on an AJAX-driven site
提问by Nite
So, I am trying to make it where every time I visit any nike.com sneaker page (without the HTML link), it automatically picks my shoe size, adds it to the cart, and checks out for me.
所以,我试图让每次我访问任何 nike.com 运动鞋页面(没有 HTML 链接)时,它都会自动选择我的鞋码,将其添加到购物车,并为我结账。
I am currently trying to use this script (below), but every time I go to the sneaker page, it does not properly add the shoe size I want, but just goes straight to checkout with nothing in my cart.
我目前正在尝试使用这个脚本(如下),但是每次我进入运动鞋页面时,它都没有正确添加我想要的鞋码,而是直接去结账,我的购物车里什么都没有。
I'm told that I need to match the code to the actual page HTML, but I don't know how to do that. Please help.
有人告诉我,我需要将代码与实际页面的 HTML 匹配,但我不知道该怎么做。请帮忙。
// ==UserScript==
// @name _Nike auto-buy(!!!) script
// @include http://*/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
var okayToClickAddtoCart = false;
//-- Assumes that size is a standard <option> tag or similar...
waitForKeyElements (".selectBox-label[value='10']", selectShoeSize);
function selectShoeSize (jNode) {
jNode.prop ('selected', true);
okayToClickAddtoCart = true;
}
waitForKeyElements (".add-to-cart.nike-button", clickAddToCart);
function clickAddToCart (jNode) {
if ( ! okayToClickAddtoCart) {
return true; //-- Don't click yet.
}
var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ('click', true, true);
jNode[0].dispatchEvent (clickEvent);
}
waitForKeyElements (".checkout-button", clickCheckoutButton);
function clickCheckoutButton (jNode) {
var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ('click', true, true);
jNode[0].dispatchEvent (clickEvent);
}
Link to the "target page"
Snapshot of the target HTML(in case the target page is removed or changed by Nike)
回答by Brock Adams
Rather than just alter the script from the question, I hope to make a quick outline of how to script these kinds of pages and actions with Greasemonkey/Tampermonkey.
我希望不只是根据问题更改脚本,而是快速概述如何使用 Greasemonkey/Tampermonkey 编写此类页面和操作的脚本。
The steps are:
步骤是:
Take careful note of what you do manually. Take special note of elements added/altered by the page's javascript, and the needed sequence of steps, if any.
Using Firebug, and/or Firefox's inspector, and/or Chrome's Developer tools, determine CSS/jQuery selector's for all of the elements you will read or manipulate. This is especially easy to do using Firebug.
Use jQuery to manipulate static HTML. Use waitForKeyElementsto handle nodes added or changed by javascript (AJAX). Use the Greasemonkey API-- which is also supported by Tampermonkey and partially supported by Chrome userscripts -- to do any cross-domain page calls, or to store any values between page loads for cross-domain sets of pages.
请仔细注意您手动执行的操作。请特别注意由页面的 javascript 添加/更改的元素,以及所需的步骤序列(如果有)。
使用Firebug和/或 Firefox 的检查器,和/或 Chrome 的开发人员工具,为您将阅读或操作的所有元素确定 CSS/jQuery 选择器。使用 Firebug 尤其容易做到这一点。
使用 jQuery 来操作静态 HTML。使用waitForKeyElements处理由 javascript (AJAX) 添加或更改的节点。使用Greasemonkey API(Tampermonkey 也支持,Chrome 用户脚本部分支持)来执行任何跨域页面调用,或存储跨域页面集的页面加载之间的任何值。
Specific example:
具体例子:
For the OP's target pages, the OP wants to: (a) automatically select the shoe size, (b) add the shoes to the shopping cart, and (c) click the checkout button.
This requires waiting for, and/or clicking on, five (5) page elements like so:
Using Firebug (or similar tool) we obtain the HTML structure for the key nodes. For example, the SIZEdropdown has HTML like this:
<div class="size-quantity"> <span class="sizeDropdown selectBox-open"> ... <label class="dropdown-label selectBox-label-showing">SIZE</label> ... <a class="selectBox size-dropdown mediumSelect footwear selectBox-dropdown" ...> ... </a> </span> </div>
Where the link actually fires off a
mousedown
event, not a click.Firebug gives us a CSS path of:
html.js body div#body div#body-wrapper.fullheight div#body-liner.clear div#content div#pdp.footwear div#product-container.clear div.pdp-buying-tools-container div.pdp-box div.buying-tools-container div#PDPBuyingTools.buying-tools-gadget form.add-to-cart-form div.product-selections div.size-quantity span.sizeDropdown a.selectBox
Which we can pare down to:
div.footwear form.add-to-cart-form span.sizeDropdown a.size-dropdown
for a reasonable selector that's likely to survive trivial page changes and unlikely to trigger on unwanted pages/products.
~~~~~~~~~~~~~
Note that Firebug also helps us see what events are attached to what, which is crucial when determining what we need to trigger. For example, for that node, I see:That link has no
href
, nor does it listen forclick
events. In this case, we must trigger amousedown
(orkeydown
).~~~~~~~~~~~~~
Using a similar process for the other 4 key nodes, we obtain CSS/jQuery selectors of:Node 1: div.footwear form.add-to-cart-form span.sizeDropdown a.size-dropdown Node 2: ul.selectBox-dropdown-menu li a:contains('10') (But this will need an additional check) Node 3: div.footwear form.add-to-cart-form span.sizeDropdown a.selectBox span.selectBox-label:contains('(10)') Node 4: div.footwear form.add-to-cart-form div.product-selections div.add-to-cart Node 5: div.mini-cart div.cart-item-data a.checkout-button:visible
Finally, we use
waitForKeyElements
to send the required events to the key nodes and to sequence through the proper order of operations.
对于OP 的目标页面,OP 想要:(a) 自动选择鞋码,(b) 将鞋子添加到购物车,以及 (c) 单击结帐按钮。
这需要等待和/或单击五 (5) 个页面元素,如下所示:
使用 Firebug(或类似工具)我们获取关键节点的 HTML 结构。例如,SIZE下拉列表的 HTML 如下所示:
<div class="size-quantity"> <span class="sizeDropdown selectBox-open"> ... <label class="dropdown-label selectBox-label-showing">SIZE</label> ... <a class="selectBox size-dropdown mediumSelect footwear selectBox-dropdown" ...> ... </a> </span> </div>
链接实际上触发了一个
mousedown
事件,而不是点击。Firebug 为我们提供了一个 CSS 路径:
html.js body div#body div#body-wrapper.fullheight div#body-liner.clear div#content div#pdp.footwear div#product-container.clear div.pdp-buying-tools-container div.pdp-box div.buying-tools-container div#PDPBuyingTools.buying-tools-gadget form.add-to-cart-form div.product-selections div.size-quantity span.sizeDropdown a.selectBox
我们可以简化为:
div.footwear form.add-to-cart-form span.sizeDropdown a.size-dropdown
对于一个合理的选择器,它可能会在微不足道的页面更改中幸存下来,并且不太可能在不需要的页面/产品上触发。
~~~~~~~~~~~~~
请注意,Firebug 还可以帮助我们查看哪些事件附加到哪些内容上,这在确定我们需要触发的内容时至关重要。例如,对于那个节点,我看到:该链接没有
href
,也不侦听click
事件。在这种情况下,我们必须触发 amousedown
(或keydown
)。~~~~~~~~~~~~~
对其他 4 个关键节点使用类似的过程,我们获得 CSS/jQuery 选择器:Node 1: div.footwear form.add-to-cart-form span.sizeDropdown a.size-dropdown Node 2: ul.selectBox-dropdown-menu li a:contains('10') (But this will need an additional check) Node 3: div.footwear form.add-to-cart-form span.sizeDropdown a.selectBox span.selectBox-label:contains('(10)') Node 4: div.footwear form.add-to-cart-form div.product-selections div.add-to-cart Node 5: div.mini-cart div.cart-item-data a.checkout-button:visible
最后,我们使用
waitForKeyElements
将所需的事件发送到关键节点并按照正确的操作顺序进行排序。
The resulting, complete, working scriptis:
生成的完整的工作脚本是:
// ==UserScript==
// @name _Nike auto-buy shoes(!!!) script
// @include http://store.nike.com/*
// @include https://store.nike.com/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
var targetShoeSize = "10";
//-- STEP 1: Activate size drop-down.
waitForKeyElements (
"div.footwear form.add-to-cart-form span.sizeDropdown a.size-dropdown",
activateSizeDropdown
);
function activateSizeDropdown (jNode) {
triggerMouseEvent (jNode[0], "mousedown");
//-- Setup step 2.
waitForKeyElements (
"ul.selectBox-dropdown-menu li a:contains('" + targetShoeSize + "'):visible",
selectDesiredShoeSize
);
}
//-- STEP 2: Select desired shoe size.
function selectDesiredShoeSize (jNode) {
/*-- Because the selector for this node is vulnerable to false positives,
we need an additional check here.
*/
if ($.trim (jNode.text () ) === targetShoeSize) {
//-- This node needs a triplex event
triggerMouseEvent (jNode[0], "mouseover");
triggerMouseEvent (jNode[0], "mousedown");
triggerMouseEvent (jNode[0], "mouseup");
//-- Setup steps 3 and 4.
waitForKeyElements (
"div.footwear form.add-to-cart-form span.sizeDropdown a.selectBox "
+ "span.selectBox-label:contains('(" + targetShoeSize + ")')",
waitForShoeSizeDisplayAndAddToCart
);
}
}
//-- STEPS 3 and 4: Wait for shoe size display and add to cart.
function waitForShoeSizeDisplayAndAddToCart (jNode) {
var addToCartButton = $(
"div.footwear form.add-to-cart-form div.product-selections div.add-to-cart"
);
triggerMouseEvent (addToCartButton[0], "click");
//-- Setup step 5.
waitForKeyElements (
"div.mini-cart div.cart-item-data a.checkout-button:visible",
clickTheCheckoutButton
);
}
//-- STEP 5: Click the checkout button.
function clickTheCheckoutButton (jNode) {
triggerMouseEvent (jNode[0], "click");
//-- All done. The checkout page should load.
}
function triggerMouseEvent (node, eventType) {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent (eventType, true, true);
node.dispatchEvent (clickEvent);
}