Instacart Ad Remover

Blocks those nasty Instacart ads on various pages, including in search, store home page, user home page, cart, etc.

  1. // ==UserScript==
  2. // @name Instacart Ad Remover
  3. // @description Blocks those nasty Instacart ads on various pages, including in search, store home page, user home page, cart, etc.
  4. // @version 70
  5. // @license MIT
  6. // @match https://*.instacart.ca/*
  7. // @match https://*.instacart.com/*
  8. // @grant GM_addStyle
  9. // @namespace https://greasyfork.org/users/1354160
  10. // ==/UserScript==
  11.  
  12. unsafeWindow.Element.prototype._attachShadow = unsafeWindow.Element.prototype.attachShadow;
  13. unsafeWindow.Element.prototype.attachShadow = function () {
  14. return this._attachShadow({
  15. mode: "open"
  16. });
  17. };
  18.  
  19. function waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals) {
  20. if (typeof waitOnce === "undefined") {
  21. waitOnce = true;
  22. }
  23. if (typeof interval === "undefined") {
  24. interval = 300;
  25. }
  26. if (typeof maxIntervals === "undefined") {
  27. maxIntervals = -1;
  28. }
  29. var targetNodes =
  30. typeof selectorOrFunction === "function" ?
  31. selectorOrFunction() :
  32. document.querySelectorAll(selectorOrFunction);
  33.  
  34. var targetsFound = targetNodes && targetNodes.length > 0;
  35. if (targetsFound) {
  36. targetNodes.forEach(function (targetNode) {
  37. var attrAlreadyFound = "data-userscript-alreadyFound";
  38. var alreadyFound = targetNode.getAttribute(attrAlreadyFound) || false;
  39. if (!alreadyFound) {
  40. var cancelFound = callback(targetNode);
  41. if (cancelFound) {
  42. targetsFound = false;
  43. } else {
  44. targetNode.setAttribute(attrAlreadyFound, true);
  45. }
  46. }
  47. });
  48. }
  49.  
  50. if (maxIntervals !== 0 && !(targetsFound && waitOnce)) {
  51. maxIntervals -= 1;
  52. setTimeout(function () {
  53. waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals);
  54. }, interval);
  55. }
  56. }
  57.  
  58. let sponsoredTexts = ["promoted", "sponsoreed", "sponsored", "spaahnserd", "spawhnserd", "spawnserd", "spaunsered", "spaunserd", "spauncered", "spauncerd", "spohnserd", "spohncerd", "spohncered", "spawncerd", "spawncered"]
  59.  
  60. function isSponsored(elem) {
  61. if (elem) {
  62. // var descendentDivs = elem.querySelectorAll('section, div')
  63.  
  64. // var sponsored = Array.from(descendentDivs).find(div => div !== null && div.shadowRoot !== null)
  65.  
  66. // if (sponsored) {
  67. // return true;
  68. // } else return false;
  69.  
  70. const sponsored = elem.querySelector('*[data-cfp-eligible]')
  71.  
  72. if (sponsored) {
  73. return true;
  74. } else return false;
  75.  
  76. } else return false
  77. }
  78.  
  79. function isSponsoredImg(img) {
  80. if (img) {
  81. let attrs = Array.from(img.attributes)
  82.  
  83. let isSponsored = attrs.find(({
  84. name,
  85. value
  86. }) => sponsoredTexts.find(txt => value.toLowerCase().includes(txt)))
  87.  
  88. // let ariaLabel = img.getAttribute('aria-label')
  89. // if (sponsoredTexts.includes(img.alt.toLowerCase().trim()) || (ariaLabel && sponsoredTexts.includes(img.getAttribute('aria-label').toLowerCase().trim()))) {
  90. if (isSponsored) {
  91. return true;
  92. } else return false;
  93.  
  94. } else return false
  95. }
  96.  
  97. function individualItems(jNode) {
  98. let li = jNode.closest('li')
  99.  
  100. if (isSponsored(li)) {
  101. let parent = li.parentNode;
  102. if (parent.tagName == 'DIV') {
  103. parent.style.display = 'none'
  104. } else {
  105. li.style.display = 'none';
  106. }
  107. }
  108. }
  109.  
  110. function undesiredElement(jNode) {
  111. jNode.style.display = 'none'
  112. }
  113.  
  114. function blockAdsInSearch() {
  115. // var lists = document.querySelectorAll('#store-wrapper .e-1yrpusx:not([style*="display:none"]):not([style*="display: none"]) > ul')
  116.  
  117. let [head, ...tail] = [].filter.call(document.querySelectorAll('#store-wrapper .e-1yrpusx:not([style*="display:none"]):not([style*="display: none"]) > ul'), function (elem) {
  118. return elem.querySelector('div[aria-label="Product"]')
  119. });
  120.  
  121. // lists.filter(list => list.querySelector('div[aria-label="Product"]') !== null)
  122. if (head) {
  123. let mainList = head
  124. let otherLists = tail.map(node => node.querySelectorAll('li'))
  125.  
  126. otherLists.forEach(itemList => itemList.forEach(item => mainList.append(item)))
  127.  
  128. tail.forEach(node => node.style.display = "none")
  129.  
  130. mainList.childNodes.forEach(item => {
  131. if (isSponsored(item)) {
  132. item.style.display = 'none';
  133. }
  134. })
  135. }
  136. }
  137.  
  138. function blockAdsInCart(jNode) {
  139. let div = jNode
  140.  
  141. if (div.innerHTML.indexOf('Suggested items') != -1) {
  142. div.style.display = 'none'
  143. }
  144. }
  145.  
  146. function homeBanner(jNode) {
  147. let carousel = jNode.closest('div[aria-label="carousel"]')
  148.  
  149. if (carousel) {
  150. carousel.style.display = "none"
  151. }
  152. }
  153.  
  154. function getbyXpath(xpath, contextNode) {
  155. let results = [];
  156. let query = document.evaluate(xpath, contextNode || document,
  157. null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  158. for (let i = 0, length = query.snapshotLength; i < length; ++i) {
  159. results.push(query.snapshotItem(i));
  160. }
  161. return results;
  162. }
  163.  
  164. function defaultTip(jNode) {
  165. const spans = jNode.querySelectorAll('span');
  166.  
  167. const otherSpan = [...spans].filter(span => span.innerHTML == 'Other')[0]
  168.  
  169. if (otherSpan) {
  170. let otherBtn = otherSpan.closest('button')
  171. otherBtn.click()
  172.  
  173. const tipDiv = document.querySelector('div[aria-label="Say thanks with a tip"]')
  174. tipDiv.querySelector('#radio-base-option-4').click()
  175.  
  176. const otherInput = tipDiv.querySelector('input[placeholder="Other amount"]')
  177. otherInput.focus();
  178.  
  179. waitForKeyElements('div[aria-label="Say thanks with a tip"] button:has(span)', function (jNode) {
  180. const btn = jNode
  181.  
  182. if (btn.innerText.includes('Continue')) {
  183. btn.click()
  184. }
  185. }, false)
  186. }
  187. }
  188.  
  189. function sponsoredCarousel(jNode) {
  190. let elem = jNode
  191.  
  192. function traverseAncestors(node) {
  193. if (node) {
  194. if (node.tagName == 'DIV') {
  195. // let spans = node.querySelectorAll('span')
  196. // let sponsoredSpans = [...spans].filter(span => span.innerHTML == ' nsored')
  197. let imgs = node.querySelectorAll('img')
  198. let sponsoredImgs = [...imgs].filter(img => isSponsoredImg(img))
  199. let individualSponsored = isSponsored(node)
  200. let scrollbars = node.querySelectorAll('.u-noscrollbar')
  201.  
  202. if ((sponsoredImgs.length > 0) && (!individualSponsored) && (scrollbars.length == 1)) {
  203. console.log(node)
  204. node.style.display = 'none';
  205. } else if (scrollbars.length > 1) {
  206. return;
  207. } else {
  208. traverseAncestors(node.parentNode);
  209. }
  210. } else traverseAncestors(node.parentNode)
  211. }
  212. }
  213.  
  214. if (!window.location.href.endsWith('homeTabForYou')) {
  215. traverseAncestors(elem.parentNode)
  216. }
  217. }
  218.  
  219. function sponsoredPlacement(jNode) {
  220. let node = jNode
  221.  
  222. let imgs = node.querySelectorAll('img')
  223. let sponsoredImgs = [...imgs].filter(img => isSponsoredImg(img))
  224.  
  225. if ((sponsoredImgs.length > 0)) {
  226. node.style.display = 'none';
  227. }
  228. }
  229.  
  230. function continueToNext(jNode) {
  231. let span = jNode
  232.  
  233. if (span.innerText === 'Continue to checkout') {
  234. setTimeout(function () {
  235. span.closest('button').click();
  236. }, 500);
  237. }
  238. }
  239.  
  240. waitForKeyElements('#store-wrapper div[aria-label="Product"]', blockAdsInSearch, false);
  241. waitForKeyElements('#store ul li div[aria-label="Product"] > div', individualItems, false);
  242. waitForKeyElements('#store-wrapper div[data-testid="regimen-section"]', undesiredElement, false);
  243. waitForKeyElements('#cart-body > div', blockAdsInCart, false);
  244. waitForKeyElements('#store-wrapper div[aria-label="Treatment Tracker modal"]', undesiredElement, false) // offer banner at bottom
  245. waitForKeyElements('#store div[aria-label="announcement"]', undesiredElement, false)
  246. waitForKeyElements('#store-wrapper div[aria-label="Tip Options"]', defaultTip, false)
  247. waitForKeyElements('#store-wrapper .u-noscrollbar', sponsoredCarousel, false)
  248. waitForKeyElements('footer span', continueToNext, false)
  249. waitForKeyElements('#storefront-placements-content article', sponsoredPlacement, false)
  250. waitForKeyElements('#store-wrapper article', sponsoredPlacement, false)
  251. waitForKeyElements('#store-wrapper div[role="region"] > section', sponsoredPlacement, false)
  252. waitForKeyElements('div[data-testid="recommendations-placements-feed"]', undesiredElement, false)