jQuery - 检测隐藏输入字段的值变化

315

我有一个隐藏的文本字段,它的值通过 AJAX 响应进行更新。

<input type="hidden" value="" name="userid" id="useid" />

当这个值发生变化时,我想触发一个AJAX请求。有没有人能指导一下如何检测变化?

我有以下代码,但不知道如何查找该值:

$('#userid').change( function() {  
    alert('Change!'); 
}) 

2
如果它通过ajax响应进行更新,为什么不在响应的成功函数中触发新的ajax请求呢? - BonyT
2
如果您在询问该值,$('#userid').val()将为您提供该值。 - BonyT
更新:当隐藏字段的值被更新时,现在会触发一个更改事件。 - Steven
9个回答

698

虽然回答有点晚,但我发现了一个答案,如果有人找到这个线程可能会有用。

对隐藏元素值的更改不会自动触发.change()事件。因此,无论您在何处设置该值,都必须告诉jQuery触发它。

function setUserID(myValue) {
     $('#userid').val(myValue)
                 .trigger('change');
}

一旦这种情况发生,

$('#userid').change(function(){
      //fire your ajax call  
})

应按预期工作。


5
在一般情况下,使用.trigger()比仅调用change()更好吗? - Hanna
1
它能运行,但似乎会触发两次更改事件!就好像如果我放入这段代码,它会触发两次,如果我删除这段代码,就不会触发任何东西。!! - Janx from Venezuela
1
为了使其与“change”事件的行为相同,您应该在setUserID()中添加代码来检查值是否真正发生了更改。if ($('#userid').val() != myVaule) { // set val() and trigger change } - nnattawat
2
如果您正在尝试通过JavaScript捕获“change”事件,那么这将非常有用,否则JavaScript所做的更改无法被.change(handler)或.on(“change”,handler)捕获。 - JJK
11
如果我不能控制改变隐藏字段值的函数(即无法触发触发器),那么这个解决方案就行不通了,对吗? - osama yaccoub

55

因为隐藏的输入不会在更改时触发“change”事件,因此我使用MutationObserver来代替触发。

(有时您无法修改某些其他脚本执行的隐藏输入值更改)

这在IE10及以下版本中不起作用。

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var trackChange = function(element) {
  var observer = new MutationObserver(function(mutations, observer) {
    if(mutations[0].attributeName == "value") {
        $(element).trigger("change");
    }
  });
  observer.observe(element, {
    attributes: true
  });
}

// Just pass an element to the function to start tracking
trackChange( $("input[name=foo]")[0] );

据我所知,MutationObserver也不会在文本字段内部的更改时触发(无论是否隐藏)。 - Ole Albers
4
非常好!@OleAlbers - OP 询问了 input[type=hidden] - Shay
1
我将其与使用jQuery的map命令检索隐藏输入字段值集合的函数结合使用。非常感谢@lulalala! - AdamJones
2
简单易懂。与“您必须在更改值时触发事件”的解决方案相反,此解决方案不依赖于程序员在更改值的点上具有代码访问权限,因此在不需要支持早于IE11版本的情况下更加有用。对我来说效果很好。谢谢。 - Joe Salazar
很棒的解决方案!谢谢!由于某些原因,trigger('change') 对我不起作用,所以我创建了一个 CustomEvent,注册它,然后使用 element.dispatchEvent(myCustomEvent) - butcarudev
这对于Chrome扩展程序也非常有用,其中内容脚本需要对隐藏字段的更改做出反应。 - Volomike

9
您可以直接使用以下函数,您也可以更改元素类型。
 $("input[type=hidden]").bind("change", function() {
       alert($(this).val()); 
 });

对隐藏元素的值进行更改不会自动触发.change()事件。因此,无论您在何处设置该值,都必须告诉jQuery触发它。

HTML

 <div id="message"></div>
<input type="hidden" id="testChange" value="0"  />    

JAVASCRIPT

var $message = $('#message');
var $testChange = $('#testChange');
var i = 1;

function updateChange() {
    $message.html($message.html() + '<p>Changed to ' + $testChange.val() + '</p>');
}

$testChange.on('change', updateChange);

setInterval(function() {
    $testChange.val(++i).trigger('change');; 
    console.log("value changed" +$testChange.val());
}, 3000);

updateChange();

应该按预期工作。

http://jsfiddle.net/7CM6k/3/


1
对我不起作用...无论如何,如何将值粘贴到隐藏字段中呢? :/ - Simon Dugré
嘿,谢谢。粘贴应该不在那里,但是更改可以检测到隐藏字段的更改事件。 - Tarun Gupta
@KrisErickson 感谢您提供的代码示例,我已经更新了代码以便能够明确地检测到更改。请查看更新后的代码示例:http://jsfiddle.net/7CM6k/3/ - Tarun Gupta
1
@TarunGupta 是的,触发器是有效的,但值没有改变。当隐藏元素的值发生变化时,浏览器不会触发change事件,你需要手动触发它。 - Kris Erickson
这个回答关于绑定所有隐藏字段的第一部分非常有帮助。谢谢! - Chad
“对隐藏元素值的更改不会自动触发.change()事件。因此,无论您在何处设置该值,都必须告诉jQuery触发它。” Hrm. 那段文字 看起来非常熟悉 - ruffin

7
$('#userid').change(function(){
  //fire your ajax call  
});

$('#userid').val(10).change();

5

Viktar 的回答 基础上,这里提供一种实现方式,您可以对一个给定的隐藏输入元素调用一次,以确保每当输入元素的值更改时都会触发后续更改事件:

/**
 * Modifies the provided hidden input so value changes to trigger events.
 *
 * After this method is called, any changes to the 'value' property of the
 * specified input will trigger a 'change' event, just like would happen
 * if the input was a text field.
 *
 * As explained in the following SO post, hidden inputs don't normally
 * trigger on-change events because the 'blur' event is responsible for
 * triggering a change event, and hidden inputs aren't focusable by virtue
 * of being hidden elements:
 * https://dev59.com/pWMm5IYBdhLWcg3wVdx5#17695525
 *
 * @param {HTMLInputElement} inputElement
 *   The DOM element for the hidden input element.
 */
function setupHiddenInputChangeListener(inputElement) {
  const propertyName = 'value';

  const {get: originalGetter, set: originalSetter} =
    findPropertyDescriptor(inputElement, propertyName);

  // We wrap this in a function factory to bind the getter and setter values
  // so later callbacks refer to the correct object, in case we use this
  // method on more than one hidden input element.
  const newPropertyDescriptor = ((_originalGetter, _originalSetter) => {
    return {
      set: function(value) {
        const currentValue = originalGetter.call(inputElement);

        // Delegate the call to the original property setter
        _originalSetter.call(inputElement, value);

        // Only fire change if the value actually changed.
        if (currentValue !== value) {
          inputElement.dispatchEvent(new Event('change'));
        }
      },

      get: function() {
        // Delegate the call to the original property getter
        return _originalGetter.call(inputElement);
      }
    }
  })(originalGetter, originalSetter);

  Object.defineProperty(inputElement, propertyName, newPropertyDescriptor);
};

/**
 * Search the inheritance tree of an object for a property descriptor.
 *
 * The property descriptor defined nearest in the inheritance hierarchy to
 * the class of the given object is returned first.
 *
 * Credit for this approach:
 * https://dev59.com/hpnga4cB1Zd3GeqPbJsC#38802602
 *
 * @param {Object} object
 * @param {String} propertyName
 *   The name of the property for which a descriptor is desired.
 *
 * @returns {PropertyDescriptor, null}
 */
function findPropertyDescriptor(object, propertyName) {
  if (object === null) {
    return null;
  }

  if (object.hasOwnProperty(propertyName)) {
    return Object.getOwnPropertyDescriptor(object, propertyName);
  }
  else {
    const parentClass = Object.getPrototypeOf(object);

    return findPropertyDescriptor(parentClass, propertyName);
  }
}

在文档准备就绪时调用此函数,如下所示:

$(document).ready(function() {
  setupHiddenInputChangeListener($('myinput')[0]);
});

在2020年,我之前的方法不再起作用了。这种方法对我来说效果最好。 - moose-o
2
你让不可能变为了可能。 真希望我能给你点赞一百万次。 - ahmednawazbutt
非常聪明!干得好 :) - waltmagic

4

可以使用Object.defineProperty()来重新定义输入元素的'value'属性并在其变化期间执行任何操作。

Object.defineProperty()允许我们为属性定义getter和setter,从而控制它。

replaceWithWrapper($("#hid1")[0], "value", function(obj, property, value) { 
  console.log("new value:", value)
});

function replaceWithWrapper(obj, property, callback) {
  Object.defineProperty(obj, property, new function() {
    var _value = obj[property];
    return {
      set: function(value) {
        _value = value;
        callback(obj, property, value)
      },
      get: function() {
        return _value;
      }
    }
  });
}

$("#hid1").val(4);

https://jsfiddle.net/bvvmhvfk/


2
我喜欢这种方法,特别是因为其他人的答案都是“自己触发变化”……这并不总是可行的。 - sMyles
1
这对我来说几乎可以工作,除了隐藏字段中的最终值实际上没有被更改,如果您检查jsfiddle,则隐藏字段值不会从“123”更改(使用Chrome)。 - MetaGuru
非常接近,但正如其他人所提到的那样,它没有保持对字段的原始mutator/setter的引用,因此更新无法影响隐藏输入本身。我正在发布一个新的解决方案,它结合了这种方法和https://dev59.com/hpnga4cB1Zd3GeqPbJsC#38802602中的方法。 - GuyPaddock

3

我从Vikars的答案开始,但是注意到当HTML表单中使用隐藏表单字段时,最后的更改没有被提交。后来我找到了Thomas的答案,因此我将两者结合起来得到了以下解决方案,这似乎对于我在提交时的隐藏表单字段也起作用:

   function replaceWithWrapper(selector, property, callback) {
      function findDescriptor(obj, prop){
         if (obj != null){
            return Object.hasOwnProperty.call(obj, prop)?
               Object.getOwnPropertyDescriptor(obj, prop):
               findDescriptor(Object.getPrototypeOf(obj), prop);
         }
      }

      jQuery(selector).each(function(idx, obj) {
         var {get, set} = findDescriptor(obj, property);

         Object.defineProperty(obj, property, {
            configurable: true,
            enumerable: true,

            get() { //overwrite getter
               var v = get.call(this);  //call the original getter
               //console.log("get '+property+':", v, this);
               return v;
            },
            
            set(v) { //same for setter
               //console.log("set '+property+':", v, this);
               set.call(this, v);
               callback(obj, property, v)
            }
         });
      });
   }

   replaceWithWrapper('#myhiddenfield', 'value', function() {
      console.log('myhiddenfield value changed!');
   });

0

这个例子将在隐藏草稿字段的值改变时(通过Chrome浏览器)返回草稿字段的值:

var h = document.querySelectorAll('input[type="hidden"][name="draft"]')[0];
//or jquery.....
//var h = $('input[type="hidden"][name="draft"]')[0];

observeDOM(h, 'n', function(draftValue){ 
  console.log('dom changed draftValue:'+draftValue);
});


var observeDOM = (function(){
var MutationObserver = window.MutationObserver || 
window.WebKitMutationObserver;

  return function(obj, thistime, callback){
    if(typeof obj === 'undefined'){
      console.log('obj is undefined');
      return;
    }

    if( MutationObserver ){

        // define a new observer
        var obs = new MutationObserver(function(mutations, observer){

            if( mutations[0].addedNodes.length || mutations[0].removedNodes.length ){

               callback('pass other observations back...');

            }else if(mutations[0].attributeName == "value" ){

               // use callback to pass back value of hidden form field                            
               callback( obj.value );

            }

        });

        // have the observer observe obj for changes in children
        // note 'attributes:true' else we can't read the input attribute value
        obs.observe( obj, { childList:true, subtree:true, attributes:true  });

       }
  };
})();

-5

虽然这个帖子已经三年了,但是这是我的解决方案:

$(function ()
{
    keep_fields_uptodate();
});

function keep_fields_uptodate()
{
    // Keep all fields up to date!
    var $inputDate = $("input[type='date']");
    $inputDate.blur(function(event)
    {
        $("input").trigger("change");
    });
}

4
模糊事件只有在输入框失去焦点时才会触发。对于隐藏的输入框来说,这不是一个有效的响应。 - manuerumx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,