admin管理员组

文章数量:1332345

I need to run a jquery ui bit of code whenever the DOM is updated via Knockout.JS. I realize this can be done using custom bindings, but that appears to be related to a specific viewmodel, I want to do it globally so whenever it happens on any viewmodel it fires?

Lets say I ALWAYS want a JQuery datepicker on all textboxes with class 'needsdate', rather than add this to each of my view models, it would be great if I could do it globally.

Is this possible?

I need to run a jquery ui bit of code whenever the DOM is updated via Knockout.JS. I realize this can be done using custom bindings, but that appears to be related to a specific viewmodel, I want to do it globally so whenever it happens on any viewmodel it fires?

Lets say I ALWAYS want a JQuery datepicker on all textboxes with class 'needsdate', rather than add this to each of my view models, it would be great if I could do it globally.

Is this possible?

Share Improve this question edited Feb 8, 2012 at 7:18 RodH257 asked Feb 8, 2012 at 0:52 RodH257RodH257 3,6325 gold badges38 silver badges46 bronze badges 4
  • Can you explain your scenario a bit more? Typically, you would use a custom binding that is bound against an observable. When that observable changes, then the binding would run whatever jQuery UI code is necessary against the element that the bindings lives on. There is potentially a way to do what you are asking, but I would want to hear more about the use case before suggesting it, as it is not the normal way to go. – RP Niemeyer Commented Feb 8, 2012 at 3:01
  • updated my main post: Lets say I always want a JQuery datepicker on all textboxes with class 'needsdate', rather than add this to each of my view models, it would be great if I could do it globally. Does that help? – RodH257 Commented Feb 8, 2012 at 7:19
  • If you are not binding to a viewmodel, then probably you don't need knockout for this. – Alexander Taran Commented Feb 8, 2012 at 7:25
  • I'm fairly sure I have the same situation, Knockout removes JQuery handles as it refreshes the UI so it's not easy to stop them from stepping on eachothers toes. The mon suggested solution is to use the $.on() method on mouseover to reapply behaviors but this doesn't work with droppable for example (hover not counted as you're dragging an element in between). The alternate is to create a binding handler, which applies JQuery whenever the binding is updated but this isn't always applicable for template bindings for example where any element in the template could be applicable to JQuery – Dead.Rabit Commented Jun 11, 2013 at 11:22
Add a ment  | 

2 Answers 2

Reset to default 7

If you are not going to be dynamically adding/removing elements, then you could just wire them up as normal. However, if you are working with dynamic content (like using an observableArray that is having its items modified), then you have a couple of options:

1- Like the answer here, you can create a custom binding. If you don't want to bind the value to a property on your view model, then you can simplify the binding to something like:

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor) {
        //initialize datepicker with some optional options
        var options = ko.utils.unwrapObservable(valueAccessor());
        $(element).datepicker(options);

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).datepicker("destroy");
        });
    }
}; 

You would place it on an element like:

<input data-bind="datepicker: { minDate: new Date() }" />

2- the other option is to use the afterRender functionality of the template (and foreach which uses template) to wire up your datepickers after new content is rendered.

I've created a refreshJQuery() function that applies all my beefy behaviors in one go. I then call this function when I subscribe to observables in my view model to update my model. And once on page load.

function refreshJQuery() {
    $(".draggable, .droppable, .resizable").each(function () {

        var $element = $(this);

        if ($element.hasClass("draggable") && !$element.data("init-draggable")) {
            $element.data("init-draggable", true).draggable(dragBehavior);
        }
        if ($element.hasClass("droppable") && !$element.data("init-droppable")) {
            $element.data("init-droppable", true).droppable(dropBehavior);
        }
        if ($element.hasClass("resizable") && !$element.data("init-resizable")) {
            $element.data("init-resizable", true).resizable(resizeBehavior);
            $(this).children().fadeTo(2000, 0);
            $element.hover(function () {
                $(this).children().fadeTo(200, 1);
            },
            function () {
                $(this).children().fadeTo(750, 0);
            });
        }
    });
};

I then use it as follows, though I've identified 2 other observables I need to call this function from also:

this.updateTime = function () {
    service.updateBookingTimes(this.id(), this.startDate(), this.endDate());
    refreshJQuery();
};

this.startDate.subscribe(this.updateTime, this);
this.endDate.subscribe(this.updateTime, this);

I've also implemented @RPNiemeyer's solution for field level JQuery subscriptions, so in addition to his sample for a datepicker, I have similar code which reapplies $.button() to <a> tags and another which converts checkboxes into iPhone style switches.

本文标签: javascriptKnockout JS event for any viewmodel dom updateStack Overflow