Pregunta

Right now, I'm binding events to the parent element of my custom tag's rendered content, then using classes to target the event onto the element which my custom tag actually renders. I feel this is likely to cause strange bugs. For instance, if anyone on my team places two custom tags using the same targeting-classes under the same immediate parent element, it would cause multiple events to fire, associated with the wrong elements.

Here's a sample of the code I'm using now:

$.views.tags({
    toggleProp: {
        template: '<span class="toggle">{{include tmpl=#content/}}</span>',
        onAfterLink: function () {
            var prop = this.tagCtx.view.data;
            $(this.parentElem).on('click', '.toggle', function () {
                prop.value(!prop.value());
            });
        },
        onDispose: function () {
            $(this.parentElem).off('click', '.toggle');
        }
    }
    // ... other custom tags simply follow the same pattern ...
});

By the time we hit onAfterLink, is there any reliable way to access the rendered DOM Element (or DOM Elements) corresponding to the custom tag itself? With no risk of hitting the wrong element by mistake? I understand that the custom tag may be text without an HTML Element, but it would still be a text node, right? (Could I even bind events to text nodes?)

In other places, and using (far) older versions of JsViews, I've bound events after the render using (sometimes a lot of) targeting logic built into the rendered elements as data- attributes. Not only is this a far more fragile method than I like for accessing the rendered data, it would be incredibly risky and convoluted to try to apply this approach to some of our deeply-nested-and-collection-ridden templates.

I also don't like needing to insert a span with my custom tag, just so I can apply classes to it, but if it's still necessary for the event, I'll cope.

I ask, then, what is a safe, modular way to bind events to the DOM so that I also have access to the data rendered directly against those elements?

Edit: As an additional concern, using onAfterLink won't let me bind events to non-data-linked rendered content. This may be part of the design intent of JsViews vs pure JsRender, but I don't yet understand why that would be the case.

¿Fue útil?

Solución

Rather than using this.parentElem, you can use

this.contents()

which is a jQuery object containing all immediate content elements within the tag.

You can also provide a selector argument,

this.contents("someselector")

to "filter" , and include an optional boolean "deep" flag to both "filter" and "find" - i.e.

this.contents("someselector", true).

Using the above APIs ensures you are only taking elements that are actually within the tag content.

You may not need to remove the handlers in onDispose, if the tag is only deleted along with its content, you can rely on the fact that jQuery will dispose handlers when the elements are removed from the DOM.

You can only attach events to elements, not to text nodes. So if your content does not include elements, you would need to add your wrapper element, but not otherwise.

$.views.tags({
    toggleProp: {
        template: '{{include tmpl=#content/}}',
        onAfterLink: function () {
            var prop = this.tagCtx.view.data;
            this.contents().on('click', function () {
                prop.value(!prop.value());
            });
        },
        onDispose: function () {
            this.contents().off('click');
        }
    }
});

Also take a look at samples such as http://www.jsviews.com/#samples/tagcontrols/tabs which use the above approach.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top