在HTML中替换数据绑定中的值而不会丢失事件

时间:2018-04-03 20:03:25

标签: javascript html5 greasemonkey tampermonkey

为了提高网站的可用性,我想用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)

但是一个页面丢失了事件而没有加载任何数据:“Price”不加载任何内容并保持空白。

然后我发现了这个:Replace text in a website

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");
  }
}

但是,不幸的是,它不适用于我的情况:它可以将“Price”替换为我想要的任何文本而不是

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>

2 个答案:

答案 0 :(得分:1)

这看起来是&#34; X Y问题&#34;。见下面的折叠...

问题意味着更换属性,而不是文本。 (而且,只是属性,以便你不打破那个由ajax驱动的页面。)

由于它是由ajax驱动的,因此您需要MutationObserverwaitForKeyElements之类的内容。

这是一个脚本,显示如何替换这些属性:

// ==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在脚本中是硬编码的。无论如何,获取实时值超出了此问题和covered elsewhere的范围。

真正的问题:

替换这些属性值不太可能实现您真正想要的(以美元显示价格)。如果页面由Knockout.js驱动(看起来似乎是),则尤其如此。

要更改显示的价格,请使用与linked answer ...

非常相似的技巧
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);
}

答案 1 :(得分:1)

如果页面使用knockout.js,我建议如下。

注意:这仅在应用绑定后才有效。如果您在此之前应用js代码,则应该单独替换绑定。就像你已经做过的那样,但要注意&#34; .toFixed(2)&#34;问题(参见Mike McCaughan的评论)。如果这就是它无法正常工作的原因,您还应该在控制台日志中看到错误。

&#13;
&#13;
$( 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);
  });
});
&#13;
<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>
&#13;
&#13;
&#13;