Замена значения в привязке данных в HTML без потери событий

Чтобы повысить удобство использования на веб-сайте, я хочу изменить следующее с помощью Greasemonkey (JavaScript):

data-bind = "text: 'Price: ' + db.totalpr().toFixed(2) + ' GBP'"`

к

data-bind = "text: 'Price: ' + db.totalpr().toFixed(2)*current_exchange_rate + ' USD'"`

Пытался

document.body.innerHTML = document.body.innerHTML.replace(text_to_find, text_to_replace)

но страница теряет события и данные не загружаются: «Цена» ничего не загружает и остается пустой.

Потом нашел это: Заменить текст на сайте

function replaceTextOnPage(from, to){
  getAllTextNodes().forEach(function(node){
    node.nodeValue = node.nodeValue.replace(new RegExp(quote(from), 'g'), to);
  });

  function getAllTextNodes(){
    var result = [];

    (function scanSubTree(node){
      if (node.childNodes.length) 
        for(var i = 0; i < node.childNodes.length; i++) 
          scanSubTree(node.childNodes[i]);
      else if (node.nodeType == Node.TEXT_NODE) 
        result.push(node);
    })(document);

    return result;
  }

  function quote(str){
    return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
  }
}

Но, к сожалению, в моем случае это не работает: он может заменить "Цена" на любой текст, который я хочу, но не на

db.totalpr().toFixed(2)

к

"db.totalpr().toFixed(2)*current_exchange_rate"

Есть идеи, как заставить его работать без потери событий?

Обновлять:

<div class = "row">
    <div class = "col-md-5">
        <h5 data-bind = "text: 'Price: ' + db.totalpr().toFixed(2) + ' GBP'" style = "margin-left:7px"></h5>
    </div>
</div>

Изменение innerHTML стирает все, единственный способ - настроить элементы напрямую, но если страница использовала атрибуты до того, как ваш скрипт дошел до нее, вы мало что можете сделать.

epascarello 03.04.2018 22:04

Например, когда я меняю «Price» на «PriceUSD», он отображается на секунду перед загрузкой данных, а затем снова меняется на «Price».

Mr D 03.04.2018 22:13

Для i18n есть действительно отличные ресурсы. Вам не нужно изобретать это колесо: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Randy Casburn 03.04.2018 22:21

Ссылка на целевую страницу. Нам нужно увидеть точно, как этот HTML размещается.

Brock Adams 03.04.2018 22:35

@BrockAdams это внутреннее - доступно только внутри компании.

Mr D 03.04.2018 22:51

Вы не можете умножить строку (результат toFixed() - строка) на число. Вы, вероятно, захотите (data.totalpr() * current_exchange_rate).toFixed(2). Однако я считаю, что @epascarello говорит о том, что вы, вероятно, не можете изменить значение атрибута таким образом. Подождите, пока значение не отобразится в DOM, затем проанализируйте значение как число и примените изменения там.

Heretic Monkey 03.04.2018 23:01

Я прав, если думаю, что на этой странице используется knockout.js?

Paul 03.04.2018 23:08
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
7
898
2

Ответы 2

Это выглядит как «проблема X Y». См. Ниже складку ...


Вопрос подразумевает замену атрибутов, а не текста. (И только атрибуты, чтобы вы не сломали страницу, управляемую ajax.)

Поскольку он управляется ajax, вам понадобится что-то вроде MutationObserver или waitForKeyElements.

Вот сценарий, который показывает, как замените такие атрибуты:

// ==UserScript==
// @name     _Dynamically replace JS-coded attributes
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @require  https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// @grant    GM.openInTab 
// ==/UserScript==
//- The @grant directives are needed to restore the proper sandbox.

var current_exchange_rate = 1.41;  //  Hard coded for demo purposes only.

waitForKeyElements ("[data-bind]", ReplacePriceAttributes);

function ReplacePriceAttributes (jNode) {
    // Following could alternatively could use `.data("bind")` since attribute is of `data-` type.
    var oldBind = jNode.attr ("data-bind");
    if (/Price:/.test (oldBind) ) {
        let newBind = oldBind.replace ("db.totalpr().toFixed(2)", `( db.totalpr() * ${current_exchange_rate} ).toFixed(2)`);
        jNode.attr ("data-bind", newBind)
    }
}

current_exchange_rate жестко запрограммирован в скрипте. Получение живых значений выходит за рамки этого вопроса и покрыт в другом месте, во всяком случае.


Настоящая проблема:

Замена этих значений атрибутов вряд ли приведет к тому, что вы действительно хотите (отображение цен в долларах США). Это особенно верно, если страница управляется Knockout.js (как кажется).

Чтобы изменить отображаемые цены, которые вы видите, используйте технику, очень похожую на связанный ответ ...

waitForKeyElements ("[data-bind]", ReplacePriceText);

function ReplacePriceText (jNode) {
    var oldPriceTxt = jNode.text ();
    /* Appropriate HTML not provided by question asker, but convert price text
        as shown in linked answer, setting newPriceTxt
    */
    jNode.text (newPriceTxt);
}

Я не уверен, сработает ли это. В случае, если это knockout.js: привязки Knockout обычно применяются при инициализации, а затем они просто обрабатываются внутри. Чтобы изменить эту привязку, вам необходимо очистить узел и повторно применить привязку к узлу с соответствующими данными.

Paul 04.04.2018 00:05

@ Пол, GTK. У вас есть страница, на которой используется что-то вроде db.totalpr(), с которым я могу поиграть?

Brock Adams 04.04.2018 00:11

Попробуйте там: knockoutjs.com/examples/helloWorld.html А пока проверю нокаутное решение. :)

Paul 04.04.2018 00:19

Я предполагаю, что ваш код теперь будет работать. :) Я не знал waitForKeyElements до сих пор, но идея мне нравится. Единственная неприятная проблема, вероятно, заключается в том, что в этом случае вам нужно проанализировать значение из данных.

Paul 04.04.2018 11:42

@BrockAdams Спасибо за ваш код! Тестировал его в течение многих часов, пытаясь понять, почему он не меняет значения - к сожалению, не смог заставить его работать, и я провел некоторую отладку: он находит правильную привязку данных, меняет то, что следует изменить, но Firefox не обновляет то, что отображается на моем экране - например, я хочу изменить «Price» на «PriceUSD»: oldBind показывает «Price», newBind показывает «PriceUSD», и на моем экране я вижу «Price», а не «PriceUSD». Мы почти на месте!

Mr D 05.04.2018 22:10

Вы пробовали код Пола? Мой код будет работать, но я не могу заполнить для вас детали, не видя фактическую страницу. Вы можете сохранить исходный HTML-код страницы оказано в какое-нибудь место, например pastebin, и поставить ссылку на него.

Brock Adams 05.04.2018 22:47

Да, видел - см. Мой комментарий под кодом Пола. К сожалению, у нас очень строгие правила относительно любой утечки данных :(

Mr D 05.04.2018 23:13

Затем вам придется самостоятельно адаптировать часть «конвертировать текст цены, как показано в связанном ответе». Это не трудно.

Brock Adams 05.04.2018 23:16

@BrockAdams Я попытался решить эту проблему с помощью общего решения, так как есть также вес и размеры - похоже на замену GBP -> USD ...

Mr D 06.04.2018 11:32

Если на странице используется knockout.js, я бы предложил следующее.

Примечание. Это работает только после применения привязок. Если вы примените свой js-код до этого, это должна сделать однократная замена привязки. Точно так же, как вы это уже сделали, но с вниманием к проблеме ".toFixed (2)" (см. Комментарий Майка МакКогана). Если это причина, по которой это не сработало, вы также должны увидеть ошибки в журнале консоли.

$( document ).ready(function() {
  // Their code. Just for demonstration.
  var viewModel = {
    db: {
      totalpr: new ko.observable(123.1234)
    }
  };
  
  ko.applyBindings(viewModel);
  
  // Your Greasemonkey code starts here:
  var current_exchange_rate = 1.41;  //  Hard coded for demo purposes only.
  
  var priceElements = $("h5[data-bind*= 'db.totalpr().toFixed(2)']")

  $.each(priceElements, function(index, value) {
    var data = ko.dataFor(value);

    // Add your new value to their model. Use ko.pureComputed to ensure its changed as soon as totalpr changes.
    data.db.modifiedTotalPr = ko.pureComputed(function () {
      return data.db.totalpr() * current_exchange_rate;
    });
    
    // Update data-bind attribute.
    $(value).attr("data-bind" , "text: 'Price: ' + db.modifiedTotalPr().toFixed(2) + ' USD'")
    
    // Apply binding.
    ko.cleanNode(value)
    ko.applyBindings(data, value);
  });
});
<link href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel = "stylesheet"/>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class = "row">
    <div class = "col-md-5">
        <h5 data-bind = "text: 'Price: ' + db.totalpr().toFixed(2) + ' GBP'" style = "margin-left:7px"></h5>
    </div>
</div>

Интересно посмотреть, отреагирует ли на это OP. И есть ли побочные эффекты при подаче завышенных цен. (Похоже, это меняет оба представления и данные.?.)

Brock Adams 04.04.2018 01:09

Если их код хорошо написан, они, вероятно, используют db.totalpr() при отправке. Код не влияет на их существующие данные. Он просто добавляет настраиваемое поле. В случае возникновения каких-либо проблем, также можно было бы написать собственную небольшую модель представления только с наблюдаемым ModifiedTotalPr и оставить данные как есть.

Paul 04.04.2018 11:39

@Paul Спасибо за ваш код! К сожалению, не могу заставить его работать: в массиве priceElements много "мусорных" элементов, а код, начинающийся с @ $ .each (priceElements, function (index, value), в моем случае никогда не выполняется ...

Mr D 05.04.2018 22:54

Массив priceelements может содержать мусор только в том случае, если селектор $ ("h5 [data-bind * = 'db.totalpr (). ToFixed (2)']") `(все h5, которые имеют привязку данных, которая содержит db.totalpr (). toFixed (2)) применяется ко многим элементам. Так ли это? Вы получаете ошибку консоли?

Paul 06.04.2018 08:46

@Paul на самом деле это 1-5 привязок данных h5 на страницу. Какую ошибку я должен получить?

Mr D 06.04.2018 14:38

Вы хотите изменить их все или только некоторые? Возможно, вам придется отрегулировать селектор, если он недостаточно конкретен. Не имеет смысла, что он не проходит через priceElements, поскольку вы упомянули, что в этом массиве есть элементы. Поэтому я бы заподозрил что-то вроде can't read db of undefined. Когда запускается ваш скрипт? До или после инициализации страницы?

Paul 07.04.2018 01:17

@Paul Сначала я хочу изменить цены - это моя цель №1, потом все остальное. can't read db of undefined не видел этого в логах. Скрипт выполняется через Greasemonkey, поэтому я не знаю, когда именно он запускается.

Mr D 09.04.2018 12:37

Не могли бы вы обновить свой вопрос текущим кодом и проверить, содержит ли priceElements какие-либо элементы?

Paul 09.04.2018 16:38

Другие вопросы по теме