Greasy Fork is available in English.

讨论 » 开发

Single-Page Application warning, how can I launch my userscript ?

§
发表于:2024-06-07
编辑于:2024-06-07

Hi, how can I launch my userscript? "window.onload = function()" not working in this situation.

Violentmonkey is showing me a warning that this webpage is a Single-Page Application: "The script didn't exist or didn't match the URL when the page loaded, which happens in Single-Page Application sites like fb or instagram that are using fake navigation. You can reload the tab to run the script To fix the script use @match for the entire site and then detect changes using MutationObserver or window.navigation API"

My userscript calculates the total cost (by including this platform cost), so the orange color number can only be higher than the original one in grey color.

Here is a webpage example: https://www.leboncoin.fr/ad/collection/2656725226 If I click on another article (for example, the projector pointed with the yellow arrow on my screenshot https://i.imgur.com/6BjA5be.png), the total price is not updated, only the original price. So sometimes, the total price is even lower than the original price, which is incorrect.

// ==UserScript==
// @name        leboncoin.fr - Calcul
// @match       *://www.leboncoin.fr/*/*.htm
// @match       *://www.leboncoin.fr/offre/*
// @match       *://www.leboncoin.fr/ad/*/*
// @grant       none
// @version     1.1
// @author      faso
// ==/UserScript==


'use strict';

window.onload = function() {
  createMeaningfulBookmarkName();
}


function createMeaningfulBookmarkName() {
  const priceElement = document.querySelectorAll('[data-qa-id="adview_price"]');
  const priceText = priceElement[1].textContent;
  const priceRegex = new RegExp(/\b(\d+,\d+|\d+)\b/);
  const priceMatch = priceText.match(priceRegex);
  const price = parseFloat(priceMatch[0].replace(',', '.'));
  const priceOnlyElement = priceElement[1].querySelector('p.text-headline-2');
    priceOnlyElement.style.color = 'gray';
    priceOnlyElement.style.fontSize = '14px';
    priceOnlyElement.style.fontWeight = 'normal';
  let charges = price * 0.04;
  if (charges < 0.99) charges = 0.99;
  const total = price + charges;
  let priceForMeStr;
  priceForMeStr = total.toLocaleString('fr-FR', {minimumFractionDigits: 0, maximumFractionDigits: 2}) + ' €';
  const totalElement = document.createElement('span');
  totalElement.innerHTML = '&nbsp;&nbsp;&nbsp;' + priceForMeStr;
    totalElement.style.color = 'orange';
    totalElement.style.fontSize = '18px';
    totalElement.style.fontWeight = 'bold';
  priceElement[1].appendChild(totalElement);
}
woxxom管理员
§
发表于:2024-06-09

As the warning says match the entire site and then monitor the URL changes or DOM changes:

// @match *://www.leboncoin.fr/*
// ==/UserScript==

if (createMeaningfulBookmarkName() === false) {
  new MutationObserver((mutations, observer) => {
    if (createMeaningfulBookmarkName() !== false) observer.disconnect();
  }).observe(document.body, {subtree: true, childList: true});
}

function createMeaningfulBookmarkName() {
  const priceElement = document.querySelectorAll('[data-qa-id="adview_price"]');
  if (!priceElement.length) return false;
.........
..........
§
发表于:2024-06-10

I have tested this suggestion (https://i.imgur.com/jy1gyiZ.png) but it is still not working:
- when the page has first loaded the page, most of the time, the userscript does not start
- when I refresh the screen, the calculation is displayed and immediately hidden after

woxxom管理员
§
发表于:2024-06-10

No, you forgot to add if (!priceElement.length) return false;

§
发表于:2024-06-10

Thanks for your feedback, I have corrected this line (https://i.imgur.com/dtNSE6n.png) but the behavior is still the same:
- when the page has first loaded the page, most of the time, the userscript does not start
- when I refresh the screen, the calculation is displayed and immediately hidden after

woxxom管理员
§
发表于:2024-06-10
编辑于:2024-06-10

The script should always run, otherwise it's a bug in the browser. What happens probably is that the site overwrites your changes. Use devtools to debug the script by adding a breakpoint at const priceElement and see what happens when it runs.

woxxom管理员
§
发表于:2024-06-10

Also try replacing that line with if (!priceElement[1]) return false;

§
发表于:2024-06-10
编辑于:2024-06-10

The script should always run, otherwise it's a bug in the browser. What happens probably is that the site overwrites your changes. Use devtools to debug the script by adding a breakpoint at const priceElement and see what happens when it runs.

When I add a breakpoint at that place I can see that everything is ok (https://i.imgur.com/VJaCoWE.png). The calculation result is even displayed as you can see, but when I press F5 at that point, I am coming again to the breakpoint (like if the webpage was relaoded) and this seems indefinite as I had already pressed F5 several times and it is always the same. When I remove the breakpoint, the webpage is first displaying the calculation then it reloads and does not display any calculation.

§
发表于:2024-06-10
if (!priceElement[1]) return false;

same result with this

woxxom管理员
§
发表于:2024-06-10

It means the site overwrites your changes. You can remove observer.disconnect(); because apparently you want to process all prices while browsing multiple pages anyway.

§
发表于:2024-06-10

It is like the site overwrites my changes. When I use the breakpoint and press F5, I am each time coming back to my breakpoint. If I do F10 I can see the proper calculation is being made and even added to the webpage.
When I disable the breakpoint, the page loads (surprisingly there is no look like it is the case when using F5. I don't understand why) but without the calculation display.

woxxom管理员
§
发表于:2024-06-10

It means the site overwrites your changes. You can remove observer.disconnect(); because apparently you want to process all prices while browsing multiple pages anyway.

§
发表于:2024-06-10

It means the site overwrites your changes. You can remove observer.disconnect(); because apparently you want to process all prices while browsing multiple pages anyway.

I have remove that `observer.disconnect();` but I still have the same annoying overwrite behavior.

woxxom管理员
§
发表于:2024-06-10
编辑于:2024-06-10

Well, now your code runs every time there's a change, including those your code makes, so you need to check for that too. Also, start the script earlier, e.g. on document-body.

// ==UserScript==
// @name        leboncoin.fr - Calcul
// @match       *://www.leboncoin.fr/*
// @grant       none
// @version     1.1
// @author      faso
// @run-at      document-body
// ==/UserScript==


'use strict';

const observer = new MutationObserver(createMeaningfulBookmarkName);
const observe = () => observer.observe(document.body, {subtree: true, childList: true});
createMeaningfulBookmarkName();
observe();

function createMeaningfulBookmarkName() {
  const priceElement = document.querySelectorAll('[data-qa-id="adview_price"]');
  if (!priceElement[1] || priceElement[1].querySelector('span[style*="orange"]')) return;
  const priceText = priceElement[1].textContent;
  const priceRegex = new RegExp(/\b(\d+,\d+|\d+)\b/);
  const priceMatch = priceText.match(priceRegex);
  if (!priceMatch) return;
  const price = parseFloat(priceMatch[0].replace(',', '.'));
  const priceOnlyElement = priceElement[1].querySelector('p.text-headline-2');
    priceOnlyElement.style.color = 'gray';
    priceOnlyElement.style.fontSize = '14px';
    priceOnlyElement.style.fontWeight = 'normal';
  let charges = price * 0.04;
  if (charges < 0.99) charges = 0.99;
  const total = price + charges;
  let priceForMeStr;
  priceForMeStr = total.toLocaleString('fr-FR', {minimumFractionDigits: 0, maximumFractionDigits: 2}) + ' €';
  const totalElement = document.createElement('span');
  totalElement.innerHTML = '&nbsp;&nbsp;&nbsp;' + priceForMeStr;
    totalElement.style.color = 'orange';
    totalElement.style.fontSize = '18px';
    totalElement.style.fontWeight = 'bold';
  observer.disconnect();
  priceElement[1].appendChild(totalElement);
  observe();
}
§
发表于:2024-06-10
编辑于:2024-06-10

Thank you. Great proposition and example, yes now it is running every time I open a webpage :)


When I click on an ad at the webpage (https://i.imgur.com/AexCqml.png), the calculation is not being updated (https://i.imgur.com/b0CA31b.png). So the calculation result is still the one of the previous watched ad. Is there a way to update it?

woxxom管理员
§
发表于:2024-06-10

If you set a DOM breakpoint on the price element you'll see the site updates nodeValue. To observe these changes you can add characterData: true to the config parameter in MutationObserver.

A more efficient approach though would be to observe the price element, not the entire document:

let priceElement;
let totalElement;

if (!waitForPrice())
  new MutationObserver(waitForPrice)
    .observe(document.body, {subtree: true, childList: true});

function waitForPrice(mutations, observer) {
  priceElement = document.querySelector('article [data-qa-id="adview_price"] p');
  if (!priceElement) return;
  observer?.disconnect();
  patchPrice();
  new MutationObserver(patchPrice).observe(priceElement, {characterData: true});
  return true;
}

function patchPrice() {
  const priceText = priceElement.textContent;
  const priceRegex = new RegExp(/\b(\d+,\d+|\d+)\b/);
  const priceMatch = priceText.match(priceRegex);
  const price = parseFloat(priceMatch[0].replace(',', '.'));
  if (priceElement.style.color !== 'gray') {
    priceElement.style.color = 'gray';
    priceElement.style.fontSize = '14px';
    priceElement.style.fontWeight = 'normal';
  }
  let charges = price * 0.04;
  if (charges < 0.99) charges = 0.99;
  const total = price + charges;
  const priceForMeStr = total.toLocaleString('fr-FR', {minimumFractionDigits: 0, maximumFractionDigits: 2}) + ' €';
  if (!totalElement) {
    totalElement = document.createElement('span');
    totalElement.style.color = 'orange';
    totalElement.style.fontSize = '18px';
    totalElement.style.fontWeight = 'bold';
    priceElement.parentElement.appendChild(totalElement);
  }
  totalElement.textContent = '\xA0\xA0\xA0' + priceForMeStr;
}

I didn't test this code, because this paranoid site blocked my IP after a couple of page reloads.

§
发表于:2024-06-18

Sorry I was full with my kids to take care. I have tested the last userscript code you have given: it is not working, the orange calculation result is displayed when loading and then immediately removed by the webpage.

With the userscript you provided before it is working but when I click on another ad inside the webpage (https://i.imgur.com/afTBEaC.png) the orange calculation is not being refreshed unfortunately, so the calculation result is incorrect. Is there a way to have it recalculate?

>I didn't test this code, because this paranoid site blocked my IP after a couple of page reloads.
I have tested, you're right, this website is checking the IP is coming from France :(

发表回复

登录以发表回复。