I have a Single Page Application with a lot of stuff in it, using durandal. On one page I have a link that leads to a different page where 3d model is rendered. OrbitControls are used to make model turn etc. That takes away my default left click and right click. After leaving that page, it still keeps mouse bindings and my left click and right click become useless for some uses like - selecting an tag meaning that input tags cannot be accessed again.
I could release bindings and reset them if I knew how. There is a deactivate function which is called when that 3d window is closed, but I have no idea what piece of code to write there. Any help would be extremely useful. I doubt any code will be of any use so I won't put any.
Thank you!
Due to request, here is simplified viewmodel:
define(['services/logger'], function (logger) {
var vm = {
attached: attached
};
return vm;
function attached(view) {
var camera, cameraTarget, scene, renderer, controls;
init();
animate();
function init() {
...
controls = new THREE.OrbitControls(camera);
...
}
function animate(){...}
function render(){...}
}
}
View is extremely complicated, but pasted here in full:
<div id="canvasDiv" style="overflow: hidden; width:100%; height:100%">
</div>
Actually, it would probably be helpful to include your code to setup OrbitControls (I'm not familiar with it).
The best approach to this whole issue would probably be to write a Knockout custom binding (google if you don't know them). A custom binding is a great place to abstract away DOM manipulation, in your case for setting up OrbitControls.
Let's assume that you have a div on which you set up OrbitControls. You could then do something like the following:
HTML:
<div data-bind="myOrbitControlsBinding: { someSetting: true; someOtherSetting: false }"></div>
JavaScript:
ko.bindingHandlers.myOrbitControlsBinding = {
init: function (element, valueAccessor) {
var settings = ko.utils.unwrapObservable(valueAccessor());
setupOrbitControlsOnElement(element, settings); // This should be your setup code for OrbitControls
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
// Here, you should unbind the event handlers for mouse clicks. How you do this depends on how OrbitControls sets them up. Please refer to their documentation for this. Maybe there is a generic dispose function?
disposeOrbitControls(element);
});
}
}
Edit:
Ah, I didn't realize you use Three. I've quickly scanned their documentation to see if they use some sort of input module that captures the events. They don't seem to. Which probably means that some where in your code, there is the keyword 'addEventListener' (search for it). This will be where the events are caught.
Your view probably has a viewmodel attached to it since you're using durandal. Inside the viewmodel, add a 'deactivate' method (and return it). In this method, you need to remove the event listener again. You probably already guessed it, but the method is called removeEventListener (see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.removeEventListener for an explanation)
I still highly recommend that you create a custom binding to setup Three. This will give you much more control over creation/deletion than you might have now. If you don't want to, make sure that Three is initialized inside the viewmodel as well, in the activate method.
Let me know if that helped, otherwise some viewmodel / three init code would be helpful.
Related
After creating and rendering a Kendo UI TreeView to fill a DIV, repeat invocation alternately renders only "loading..." or works properly. Since I am having possibly similar problems with Kendo UI ContextMenu, I speculate there may be some required cleanup in between, which is passively done by even invocations such that odd invocations work, but I can't figure it out (a link to Kendo UI docs I might be missing so I can understand why I've missed this would be appreciated to help with other issues).
In my JSFiddle example, click "draw" over and over and you'll see the alternate behavior. Speculatively clicking "draw, destroy, draw, destroy..." does not seem to help.
https://jsfiddle.net/rk3nfnnu/
<script>
function TreeDestroy() { // http://stackoverflow.com/questions/5431351
$('#Tree_Space').data('kendoTreeView').destroy();
alert('destroyed');
}
function TreeShow() {
$('#Tree_Space').kendoTreeView({
dataSource: [ { Name: 'Top', items: [ { Name:'Item' } ] } ],
template: kendo.template($('#Tree_template').html())
});
alert('shown');
}
</script>
draw |
destroy
<div id='Tree_Space'>
</div>
<script type='text/x-kendo-template' id='Tree_template'>
#= item.Name#
</script>
I have updated that fiddle. The destroy(); method probably only destroys allocated dom elements after the widget was rendered (the nodes). I doubt it cleans up the wrappers and whatnot. In your TreeDestroy(), issue a clear on that element div. Of course, you should call TreeDestroy prior to TreeCreate just in case.
function TreeDestroy() { // http://stackoverflow.com/questions/5431351
$('#Tree_Space').data('kendoTreeView').destroy();
$('#Tree_Space').html('');
alert('destroyed');
}
Here is some kendoui documentation that refers to how to handle manual deletion of widgets.
I am new to ember, but have almost finished what I set out to do. I just need to animate things as the final step.
I am using animate.css and animateCSS for my animations.
I have a set of tags. When the user clicks on each button, i call the action onBtnClicked in my controller. When this button is clicked, I basically change the selectedImage property on my controller to a different URL. The selectedImage property automatically updates a tag and the image is changed on the website.
What I would like to do is when the user clicks on the button, to run an animation to transitionOut the selectedImage and transitionIn the new image.
I am able to execute the animations, but the selectedImage updates too quickly and thus the new image is transitionedOut and transitionedIn.
If you have any advice on how to effectively handle the transition out animation it would be greatly aprpeciated.
You can simply first run transitionOut animation, and only when it's complete set the property. Something like this:
actions: {
select: function(item) {
var me = this;
Ember.$("img").fadeOut(function() {
me.set('currentImage', item);
Ember.$("img").fadeIn();
});
}
}
I've made a small demo with jQuery animations (animateCSS seem to use same callbacks) http://emberjs.jsbin.com/hiyatajeve
Out of the box Ember doesn't support animated transitions since when you transition out, it just removes the template partial from the DOM. And Ember.View hooks doesn't support promises.
I personally don't like managing views from controllers, this is another abstraction layer.
So I would recommend you to use Liquid Fire Addon which makes Ember to handle animated transitions in native way.
hey guys i am very new to js and jquery in genenral and i was just going throught the plugin code of a gallery plugin , i can across the function called _loadevents , that had the following content , see below :
this.$navPrev.on('click.gallery', function (event) {
});
this.$navNext.on('click.gallery', function (event) {
});
this.$wrapper.on('webkitTransitionEnd.gallery transitionend.gallery OTransitionEnd.gallery', function (event) {
});
now $navPrev , $navNext , and $wrapper are obviously some HTML element , now my question is about another method i came across in the same plugin , look below :
destroy: function () {
// console.log('inside destroy');
this.$navPrev.off('.gallery');
this.$navNext.off('.gallery');
this.$wrapper.off('.gallery');
}
now i see that if this function is called all the event handlers will be taken off. now , can somebody tell me what is the necessacity of such a function , does it improve a plugins efficiency ? how or when does such a function get used and is it a common practice to write e destroy function for events in plugins ?
Thank you.
Alex-z .
Destroy functions in plugins enable a developer to reset or remove a plugin from an element, restoring the element to before the plugin was initialised. This is useful if, for example, you have a gallery plugin that works and looks fantastic on desktop, but you don't want it on mobile. You can listen to resize event on window and if the window size is smaller than e.g. 710px then destroy the plugin. This will remove all the added events, undo any DOM manipulation, and restore the html elements back to how they were before the plugin was first initialised (turn-wise, if the window size is larger than 710px then initialise the plugin).
They are generally considered good practice.
I'm not sure why I can't get the button element using my UI hash. This is what my Layout looks like:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
ui: {
btnSave: "#btnSave"
},
events: {
"click #ui.btnSave": "onSave"
},
onInitialize: function () {
this.listenTo(App.vent, "DisableSaveButton", function(val) {
this.disableSaveButton(val);
},this);
},
disableSaveButton: function () {
this.ui.btnSave.prop("disabled",val).toggleClass("ui-state-disabled",val);
},
onSave: function () {
alert("saved!");
}
})
In VS2013, when my breakpoint hits the line inside disableSaveButton method, I entered $("#btnSave") into the Watch window and I was able to get the element back. I could tell because it had a length of 1. From this, I know the button is rendered. However, if I enter this.ui.btnSave into the Watch window, I would get an element with length of 0.
My BaseLayout object is basically a custom object extended from Marionette.Layout
Marionette version: 1.8.8
Any ideas why I can't find the button element using this.ui.btnSave?
Thanks in advance!
Got some help from a coworker and the issue might be because the element is out of scope. Basically, inside the Layout object, 'this' does not contain the element. We were able replace 'this.ui.btnSave' with '$("#btnSave",this.buttonset.el)' and that works fine. buttonset is the region that actually contains the html element.
This seems like an inconsistency because even though the ui hash didn't work, the click event utilizing the ui hash did work.
UPDATE 6/3/2015:
Another coworker of mine provided a better solution. Basically, in my Layout I use a display function to display my view. It looks something like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
}
})
Basically, I'm saying to set the html of my region, which is this.buttonset.el, to my template's html. As of now, my layout doesn't know any of the elements inside the region. It just contains a region which displays the elements. So there is some sort of disconnect between my layout and the elements in my region.
The correct solution, as opposed to my earlier workaround, is to simply add the following line of code at the end:
this.bindUIElements();
From Marionette Annotated Source:
This method binds the elements specified in the “ui” hash inside the
view’s code with the associated jQuery selectors.
So this final code looks like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
this.bindUIElements();
}
})
With this, I was able to finally able to retrieve my element using this.ui.btnSave.
I'm using ExtJS 3.2.1 and I need a component almost identical to the bundled HtmlEditor, with one exception: it must start editing the HTML source code directly. The reason I don't use a normal TextArea is that the user should be able to preview the result of his actions before submitting.
I've tried calling toggleSourceEdit(), as per ExtJS documentation, with no success. Debugging, I see that the editor object has the sourceEditMode property set to true, and the Source Edit button seems as if it was "pressed", but clicking on it does not render the typed HTML, and clicking it again goes to the Source Mode.
I've tried calling toggleSourceEdit() after the container show() method, on the container afterLayout listener and on the editor afterRender listener. I've tried also calling it on another button that I added to the container. The result is the same on every try.
The only other option I see is updating ExtJS to 3.3.0, but I haven't seem anything related on the changelogs. Either way, it's going to be my next step. EDIT: The app had another problems when updating, we'll make a bigger effort to update later. As of right now, we are using the HtmlEditor in its original setting.
Thanks!
ran into the same problem (using 3.3.0 by the way)
stumbled upon a fix by dumb luck. i have no idea why this works, but second time is the charm. call it twice in a row to achieve the desired effect..
HTMLEditor.toggleSourceEdit(true);
HTMLEditor.toggleSourceEdit(true);
hope that helps!
Rather calling toggleSourceEdit(), try to setup the configuration while you create HtmlEditor Object
Using toggleSourceEdit() caused some problems for me. One was that this seemed to put the editor somewhere in limbo between source edit and WYSIWYG mode unless I used a timeout of 250ms or so. It also puts the focus in that editor, and I don't want to start the form's focus in the editor, especially since it's below the fold and the browser scrolls to the focused html editor when it opens.
The only thing that worked for me was to extend Ext.form.HtmlEditor and then overwrite toggleSourceEdit, removing the focus command. Then adding a listener for toggling to the source editor when the component is initialized. This is for Ext 4.1 and up. For older versions, replace me.updateLayout() with me.doComponentLayout().
var Namespace = {
SourceEditor: Ext.define('Namespace.SourceEditor', {
extend: 'Ext.form.HtmlEditor',
alias: 'widget.sourceeditor',
initComponent: function() {
this.callParent(arguments);
},
toggleSourceEdit: function (sourceEditMode) {
var me = this,
iframe = me.iframeEl,
textarea = me.textareaEl,
hiddenCls = Ext.baseCSSPrefix + 'hidden',
btn = me.getToolbar().getComponent('sourceedit');
if (!Ext.isBoolean(sourceEditMode)) {
sourceEditMode = !me.sourceEditMode;
}
me.sourceEditMode = sourceEditMode;
if (btn.pressed !== sourceEditMode) {
btn.toggle(sourceEditMode);
}
if (sourceEditMode) {
me.disableItems(true);
me.syncValue();
iframe.addCls(hiddenCls);
textarea.removeCls(hiddenCls);
textarea.dom.removeAttribute('tabindex');
//textarea.focus();
me.inputEl = textarea;
} else {
if (me.initialized) {
me.disableItems(me.readOnly);
}
me.pushValue();
iframe.removeCls(hiddenCls);
textarea.addCls(hiddenCls);
textarea.dom.setAttribute('tabindex', -1);
me.deferFocus();
me.inputEl = iframe;
}
me.fireEvent('editmodechange', me, sourceEditMode);
me.updateLayout();
}
})
}
Then to use it:
Ext.create('Namespace.SourceEditor', {
/*regular options*/
listeners: {
initialize: function(thisEditor) {
thisEditor.toggleSourceEdit();
}
}
});
htmlEditor.toggleSourceEdit(true);
one time should be enough if you do this listening to the afterrender event of the editor.