Чтобы повысить удобство использования на веб-сайте, я хочу изменить следующее с помощью 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>
Например, когда я меняю «Price» на «PriceUSD», он отображается на секунду перед загрузкой данных, а затем снова меняется на «Price».
Для i18n есть действительно отличные ресурсы. Вам не нужно изобретать это колесо: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Ссылка на целевую страницу. Нам нужно увидеть точно, как этот HTML размещается.
@BrockAdams это внутреннее - доступно только внутри компании.
Вы не можете умножить строку (результат toFixed() - строка) на число. Вы, вероятно, захотите (data.totalpr() * current_exchange_rate).toFixed(2). Однако я считаю, что @epascarello говорит о том, что вы, вероятно, не можете изменить значение атрибута таким образом. Подождите, пока значение не отобразится в DOM, затем проанализируйте значение как число и примените изменения там.
Я прав, если думаю, что на этой странице используется knockout.js?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Это выглядит как «проблема 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 обычно применяются при инициализации, а затем они просто обрабатываются внутри. Чтобы изменить эту привязку, вам необходимо очистить узел и повторно применить привязку к узлу с соответствующими данными.
@ Пол, GTK. У вас есть страница, на которой используется что-то вроде db.totalpr(), с которым я могу поиграть?
Попробуйте там: knockoutjs.com/examples/helloWorld.html А пока проверю нокаутное решение. :)
Я предполагаю, что ваш код теперь будет работать. :) Я не знал waitForKeyElements до сих пор, но идея мне нравится. Единственная неприятная проблема, вероятно, заключается в том, что в этом случае вам нужно проанализировать значение из данных.
@BrockAdams Спасибо за ваш код! Тестировал его в течение многих часов, пытаясь понять, почему он не меняет значения - к сожалению, не смог заставить его работать, и я провел некоторую отладку: он находит правильную привязку данных, меняет то, что следует изменить, но Firefox не обновляет то, что отображается на моем экране - например, я хочу изменить «Price» на «PriceUSD»: oldBind показывает «Price», newBind показывает «PriceUSD», и на моем экране я вижу «Price», а не «PriceUSD». Мы почти на месте!
Вы пробовали код Пола? Мой код будет работать, но я не могу заполнить для вас детали, не видя фактическую страницу. Вы можете сохранить исходный HTML-код страницы оказано в какое-нибудь место, например pastebin, и поставить ссылку на него.
Да, видел - см. Мой комментарий под кодом Пола. К сожалению, у нас очень строгие правила относительно любой утечки данных :(
Затем вам придется самостоятельно адаптировать часть «конвертировать текст цены, как показано в связанном ответе». Это не трудно.
@BrockAdams Я попытался решить эту проблему с помощью общего решения, так как есть также вес и размеры - похоже на замену GBP -> USD ...
Если на странице используется 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. И есть ли побочные эффекты при подаче завышенных цен. (Похоже, это меняет оба представления и данные.?.)
Если их код хорошо написан, они, вероятно, используют db.totalpr() при отправке. Код не влияет на их существующие данные. Он просто добавляет настраиваемое поле. В случае возникновения каких-либо проблем, также можно было бы написать собственную небольшую модель представления только с наблюдаемым ModifiedTotalPr и оставить данные как есть.
@Paul Спасибо за ваш код! К сожалению, не могу заставить его работать: в массиве priceElements много "мусорных" элементов, а код, начинающийся с @ $ .each (priceElements, function (index, value), в моем случае никогда не выполняется ...
Массив priceelements может содержать мусор только в том случае, если селектор $ ("h5 [data-bind * = 'db.totalpr (). ToFixed (2)']") `(все h5, которые имеют привязку данных, которая содержит db.totalpr (). toFixed (2)) применяется ко многим элементам. Так ли это? Вы получаете ошибку консоли?
@Paul на самом деле это 1-5 привязок данных h5 на страницу. Какую ошибку я должен получить?
Вы хотите изменить их все или только некоторые? Возможно, вам придется отрегулировать селектор, если он недостаточно конкретен. Не имеет смысла, что он не проходит через priceElements, поскольку вы упомянули, что в этом массиве есть элементы. Поэтому я бы заподозрил что-то вроде can't read db of undefined. Когда запускается ваш скрипт? До или после инициализации страницы?
@Paul Сначала я хочу изменить цены - это моя цель №1, потом все остальное. can't read db of undefined не видел этого в логах. Скрипт выполняется через Greasemonkey, поэтому я не знаю, когда именно он запускается.
Не могли бы вы обновить свой вопрос текущим кодом и проверить, содержит ли priceElements какие-либо элементы?
Изменение innerHTML стирает все, единственный способ - настроить элементы напрямую, но если страница использовала атрибуты до того, как ваш скрипт дошел до нее, вы мало что можете сделать.