Javascript JQuery元素自定义淘汰customBinding

Javascript JQuery元素自定义淘汰customBinding,javascript,jquery,knockout.js,coffeescript,custom-binding,Javascript,Jquery,Knockout.js,Coffeescript,Custom Binding,我正在尝试对jQuery对象进行剔除绑定,该对象能够了解dom的任何更改(属性、文本、html、追加、删除等) 我正在进行一项调查 正如JSFIDLE所明确指出的问题,jquery不会跟踪dom中的重复项,而是移动元素。因此,如果我对同一个jq元素有多个绑定,jQuery将只能将其放在一个数据绑定中。我认为可能有一种方法可以创建一个jqObservable,它保存原始jquery元素,然后分发该元素的副本。无论何时通知onMutate,它都会更新每个副本。当然,我需要一个inception拦截器

我正在尝试对jQuery对象进行剔除绑定,该对象能够了解dom的任何更改(属性、文本、html、追加、删除等)

我正在进行一项调查

正如JSFIDLE所明确指出的问题,jquery不会跟踪dom中的重复项,而是移动元素。因此,如果我对同一个jq元素有多个绑定,jQuery将只能将其放在一个数据绑定中。我认为可能有一种方法可以创建一个jqObservable,它保存原始jquery元素,然后分发该元素的副本。无论何时通知onMutate,它都会更新每个副本。当然,我需要一个inception拦截器(数据绑定到已绑定元素下的同一元素)。我认为og的一个问题是知道哪个subsection正在调用observable来知道要返回jquery元素的哪个副本,这样它就不会返回错误的副本,并且知道销毁已经解除绑定/取消订阅的元素。另外,当其中一个jquery副本被修改时,我需要更新其余的jquery副本。顺便说一下,我不在乎它是否在jQuery函数之外被修改,只有jQuery函数应该在这个插件的元素上被调用

任何建议都将不胜感激

附言

OnMutate是我自己定制的jQuery插件

我最初试图订阅更改事件,但除了输入之外,这对其他任何东西都不起作用。没有顶级订阅子树修改,最近的事情是每秒刷新一次以检查html中的差异。另一个选择是我在stack overflow上看到的另一个插件

虽然它只适用于少数几个函数,但它给了我为所有需要它们的元素创建回调的想法,仅此而已,所以我创建了这个函数来自动为我想要的任意多个jQuery函数和任何其他插件创建回调。这是我在下面创建的

(($, window) ->
  hasArgs = () -> arguments.length > 0
  noArgs = () -> arguments.length == 0
  class DomCallbacks
    ###*
     * Defines the jQuery functions to create callbacks for, and which type of callback they contain
     * @type {defaults}
     *   @properties {array of jQuery function names, and test to determine if the callback 
     *   should be run based on input to the jQuery function, passed the same arguments that the jQuery
     *   function is}
    ###
    defaults:
      Mutate: 
        jqfuncs: ["addClass","append","appendTo","attr","clone","css","detach","empty","hasClass","height","html","innerHeight","innerWidth","insertAfter","insertBefore","offset","outerHeight","outerWidth","position","prepend","prependTo","prop","remove","removeAttr","removeClass","removeProp","replaceAll","replaceWith","scrollLeft","scrollTop","text","toggleClass","unwrap","val","width","wrap","wrapAll","wrapInner"]
        defaultCheck: hasArgs
      Look: 
        jqfuncs:["addClass","after",'append',"appendTo","attr","before","clone","css","detach","empty","hasClass","height","html","innerHeight","innerWidth","insertAfter","insertBefore","offset","outerHeight","outerWidth","position","prepend","prependTo","prop","remove","removeAttr","removeClass","removeProp","replaceAll","replaceWith","scrollLeft","scrollTop","text","toggleClass","unwrap","val","width","wrap","wrapAll","wrapInner"]
        defaultCheck: noArgs

    constructor: (options) ->
      @categories = $.extend({}, @defaults, options)

    originals: {} 

    ###*
     * Creates a function in jQuery's function object by replacing the name with a wrapper function
     * and then calling the original function, and has the new function call each category of callback
     * for the starting element and it's parents
     * @param {string} name       the name/key of the function being replaced
     * @param {string} categories the type of callback that the function should notify
    ###
    addCallback: (name, categories) ->
      pairs = (obj) ->
        pairs = []
        for key of obj 
          if (hasOwnProperty.call(obj, key))
            pairs.push([key, obj[key]])
        pairs

      if !@originals[name]?
        @originals[name] = $.fn[name]
      _this = @

      categories = $.grep pairs(categories), (pair) ->
        $.grep(pair[1].jqfuncs, (val) -> val.name == name || val == name).length > 0
      ###*
       * The actual wrapper function being created
       * @return {object} returns the original functions return
      ###
      $.fn[name] =  () ->
        options = {args: arguments}
        ittr = []
        if !$.fn[name].peek          
          ittrArray = for category in categories
            key = category[0]
            val = category[1]
            funcs = ($.grep(val.jqfuncs, (checking) -> checking.name == name || checking == name)).slice(0,1)[0].check
            {key:key, val: val ,funcs:funcs}

          for ittr in ittrArray
            if !ittr.funcs?
              ittr.funcs = ittr.val.defaultCheck
            if !ittr.funcs? or ittr.funcs.apply(@,arguments)
              @["pre"+ittr.key](null, options)
              @parents()["pre"+ittr.key](null, options)

        funcRet = _this.originals[name].apply(@, arguments)
        options.funcRet = funcRet

        if !$.fn[name].peek
          for ittr in ittrArray
            if !ittr.funcs?
              ittr.funcs = ittr.val.defaultCheck
            if !ittr.funcs? or ittr.funcs.apply(@,arguments)
              @["on"+ittr.key](null, options)
              @parents()["on"+ittr.key](null, options)

        funcRet

      $.fn[name].peek = false

    ###*
     * restores the original function to it's property in the jQuery function object
     * @param  {string} name key used to find function to restore
     * @return {[type]}      [description]
    ###
    removeCallback: (name) ->
      if @originals[name]?
        $.fn[name] = @originals[name]
        delete @originals[name]  

  ###*
   * Creates a jQuery plugin capable of subscribing to and calling callbacks
   * for each node
   * @param  {string} precedence names the function to run before or after the subscripton function
   * @param  {string} category   the name of the type of
   * @return {function}          returns the function capable of setting and running callbacks for individual elements
  ###
  createCallbackPlugin = (precedence, category) ->
    ###*
     * A jQuery plugin which creates a sets callbacks for elements and calls them if none are passed in
     * @param  {function} callback the callback to be set (optional)
     * @param  {object}   options  options to be sent to the callback, set by the trigger function
     * @return {jQuery}            returns jQuery object for chaining
    ###
    return (callback, options) ->
      @each (i, el)  ->
        if callback?
          if !el['domCallbacks']?
            el['domCallbacks'] = {}
          if !el['domCallbacks']['_'+precedence+category]?
            el['domCallbacks']['_'+precedence+category] = []
          el['domCallbacks']['_'+precedence+category].push callback
        else
          if el['domCallbacks']? && el['domCallbacks']['_'+precedence+category]?
            for cb in el['domCallbacks']['_'+precedence+category]
              cb(options, el)
        @
      @

  domCallbacks = new DomCallbacks()

  ###*
   * This creates each function inside of jQuery capable of setting and running callbacks on
   * individual elements.
   * @param  {[type]} options [description]
   * @return {[type]}         [description]
  ###
  $.extend createDomCallbacks: (options) ->
    $.extend({}, domCallbacks.categories, options)
    categoryFunctions = {}
    for key, category of domCallbacks.categories
      categoryFunctions['pre'+key] = createCallbackPlugin('pre',key)
      categoryFunctions['on'+key] = createCallbackPlugin('on',key)
      $.extend $.fn, categoryFunctions
    for key, category of domCallbacks.categories
      for val in category.jqfuncs
        domCallbacks.addCallback( (if val.name then val.name else val) , domCallbacks.categories)

    ###*
     * removes any callbacks each element in the jquery object
     * @param  {object} options an object conatining keys for the type of callback to be removed (e.g. options = {Mutate: {}})
     * @return {[type]}         returns jquery object for chaining
    ###
    $.extend $.fn, removeDomCallbacks: (options) ->
      @each (i, el) ->
        if el['domCallbacks']?
          if !options?
            delete el['domCallbacks']
          for category of options
            delete el['domCallbacks']["_pre"+category]
            delete el['domCallbacks']["_on"+category]

        @
      @
    ###*
     * turns off all callbacks for performing actions without notifying up the dom
     * @return {jQuery} for chaining
    ###
    $.extend $.fn, peek: () ->
      for key,val of domCallbacks.originals
        $.fn[key].peek = true
      @each (i, el) ->
        @
      @

    ###*
     * turns off all callbacks for performing actions without notifying up the dom
     * @return {jQuery} for chaining
    ###
    $.extend $.fn, unpeek: () ->
      for key,val of domCallbacks.originals
        $.fn[key].peek = false
      @each (i, el) ->
        @
      @

) @jQuery, @

好吧,这是不久前的一个例子:。你的意图是什么?这将有助于解释,因为如果人们了解你想要完成的事情(你如何尝试使用你所写的内容),你可能会得到更多关于如何更有效地完成你想要的事情的反馈。@Cymen只是想让你知道我更新了我的答案
(($, window) ->
  hasArgs = () -> arguments.length > 0
  noArgs = () -> arguments.length == 0
  class DomCallbacks
    ###*
     * Defines the jQuery functions to create callbacks for, and which type of callback they contain
     * @type {defaults}
     *   @properties {array of jQuery function names, and test to determine if the callback 
     *   should be run based on input to the jQuery function, passed the same arguments that the jQuery
     *   function is}
    ###
    defaults:
      Mutate: 
        jqfuncs: ["addClass","append","appendTo","attr","clone","css","detach","empty","hasClass","height","html","innerHeight","innerWidth","insertAfter","insertBefore","offset","outerHeight","outerWidth","position","prepend","prependTo","prop","remove","removeAttr","removeClass","removeProp","replaceAll","replaceWith","scrollLeft","scrollTop","text","toggleClass","unwrap","val","width","wrap","wrapAll","wrapInner"]
        defaultCheck: hasArgs
      Look: 
        jqfuncs:["addClass","after",'append',"appendTo","attr","before","clone","css","detach","empty","hasClass","height","html","innerHeight","innerWidth","insertAfter","insertBefore","offset","outerHeight","outerWidth","position","prepend","prependTo","prop","remove","removeAttr","removeClass","removeProp","replaceAll","replaceWith","scrollLeft","scrollTop","text","toggleClass","unwrap","val","width","wrap","wrapAll","wrapInner"]
        defaultCheck: noArgs

    constructor: (options) ->
      @categories = $.extend({}, @defaults, options)

    originals: {} 

    ###*
     * Creates a function in jQuery's function object by replacing the name with a wrapper function
     * and then calling the original function, and has the new function call each category of callback
     * for the starting element and it's parents
     * @param {string} name       the name/key of the function being replaced
     * @param {string} categories the type of callback that the function should notify
    ###
    addCallback: (name, categories) ->
      pairs = (obj) ->
        pairs = []
        for key of obj 
          if (hasOwnProperty.call(obj, key))
            pairs.push([key, obj[key]])
        pairs

      if !@originals[name]?
        @originals[name] = $.fn[name]
      _this = @

      categories = $.grep pairs(categories), (pair) ->
        $.grep(pair[1].jqfuncs, (val) -> val.name == name || val == name).length > 0
      ###*
       * The actual wrapper function being created
       * @return {object} returns the original functions return
      ###
      $.fn[name] =  () ->
        options = {args: arguments}
        ittr = []
        if !$.fn[name].peek          
          ittrArray = for category in categories
            key = category[0]
            val = category[1]
            funcs = ($.grep(val.jqfuncs, (checking) -> checking.name == name || checking == name)).slice(0,1)[0].check
            {key:key, val: val ,funcs:funcs}

          for ittr in ittrArray
            if !ittr.funcs?
              ittr.funcs = ittr.val.defaultCheck
            if !ittr.funcs? or ittr.funcs.apply(@,arguments)
              @["pre"+ittr.key](null, options)
              @parents()["pre"+ittr.key](null, options)

        funcRet = _this.originals[name].apply(@, arguments)
        options.funcRet = funcRet

        if !$.fn[name].peek
          for ittr in ittrArray
            if !ittr.funcs?
              ittr.funcs = ittr.val.defaultCheck
            if !ittr.funcs? or ittr.funcs.apply(@,arguments)
              @["on"+ittr.key](null, options)
              @parents()["on"+ittr.key](null, options)

        funcRet

      $.fn[name].peek = false

    ###*
     * restores the original function to it's property in the jQuery function object
     * @param  {string} name key used to find function to restore
     * @return {[type]}      [description]
    ###
    removeCallback: (name) ->
      if @originals[name]?
        $.fn[name] = @originals[name]
        delete @originals[name]  

  ###*
   * Creates a jQuery plugin capable of subscribing to and calling callbacks
   * for each node
   * @param  {string} precedence names the function to run before or after the subscripton function
   * @param  {string} category   the name of the type of
   * @return {function}          returns the function capable of setting and running callbacks for individual elements
  ###
  createCallbackPlugin = (precedence, category) ->
    ###*
     * A jQuery plugin which creates a sets callbacks for elements and calls them if none are passed in
     * @param  {function} callback the callback to be set (optional)
     * @param  {object}   options  options to be sent to the callback, set by the trigger function
     * @return {jQuery}            returns jQuery object for chaining
    ###
    return (callback, options) ->
      @each (i, el)  ->
        if callback?
          if !el['domCallbacks']?
            el['domCallbacks'] = {}
          if !el['domCallbacks']['_'+precedence+category]?
            el['domCallbacks']['_'+precedence+category] = []
          el['domCallbacks']['_'+precedence+category].push callback
        else
          if el['domCallbacks']? && el['domCallbacks']['_'+precedence+category]?
            for cb in el['domCallbacks']['_'+precedence+category]
              cb(options, el)
        @
      @

  domCallbacks = new DomCallbacks()

  ###*
   * This creates each function inside of jQuery capable of setting and running callbacks on
   * individual elements.
   * @param  {[type]} options [description]
   * @return {[type]}         [description]
  ###
  $.extend createDomCallbacks: (options) ->
    $.extend({}, domCallbacks.categories, options)
    categoryFunctions = {}
    for key, category of domCallbacks.categories
      categoryFunctions['pre'+key] = createCallbackPlugin('pre',key)
      categoryFunctions['on'+key] = createCallbackPlugin('on',key)
      $.extend $.fn, categoryFunctions
    for key, category of domCallbacks.categories
      for val in category.jqfuncs
        domCallbacks.addCallback( (if val.name then val.name else val) , domCallbacks.categories)

    ###*
     * removes any callbacks each element in the jquery object
     * @param  {object} options an object conatining keys for the type of callback to be removed (e.g. options = {Mutate: {}})
     * @return {[type]}         returns jquery object for chaining
    ###
    $.extend $.fn, removeDomCallbacks: (options) ->
      @each (i, el) ->
        if el['domCallbacks']?
          if !options?
            delete el['domCallbacks']
          for category of options
            delete el['domCallbacks']["_pre"+category]
            delete el['domCallbacks']["_on"+category]

        @
      @
    ###*
     * turns off all callbacks for performing actions without notifying up the dom
     * @return {jQuery} for chaining
    ###
    $.extend $.fn, peek: () ->
      for key,val of domCallbacks.originals
        $.fn[key].peek = true
      @each (i, el) ->
        @
      @

    ###*
     * turns off all callbacks for performing actions without notifying up the dom
     * @return {jQuery} for chaining
    ###
    $.extend $.fn, unpeek: () ->
      for key,val of domCallbacks.originals
        $.fn[key].peek = false
      @each (i, el) ->
        @
      @

) @jQuery, @