ExtJs: Tree: how to scroll directly to a specific element? - javascript

I'm looking for a way to scroll to a specific element in a Tree.
Any idea how to do this ?

Try this in your node click event handler:
node.getUI().getIconEl().scrollIntoView(node.getOwnerTree(), false);

I just had to call n.select(); but I had to do it in the right place :)
I made a TreeLoader, and linked it to my tree. In its event load (which means 'when all is downloaded', I would have called it afterload...), I read all the nodes and depending on their id I act consequently:
var LasTreeLoader = new Ext.tree.TreeLoader({
dataUrl: 'json/las.php',
listeners: {
load: function(loader,n,response) {
console.log('datas downloaded');
n.eachChild(
function(n) {
if ((n.id=='las/2007') ||
(n.id=='las/2007/08') ||
(n.id=='las/2007/08/29')) {
n.expand(false,false);
}
if (n.id=='las/2007/08/29/21_14_04') {
n.select();
}
});
}
}
});

The DOM scrollIntoView doesn't really work as expected as it always either top or bottom aligns the element. ExtJS however seems to have just what is needed:
Ext.get(el|elId).scrollIntoView(containerId|containerEl);
For example, to ensure the currently selected item in an Ext.view.View (dataview) instance is visible, I did:
Ext.define('MyView', {
extend: 'Ext.view.View',
...
listeners: {
'selectionchange': function(_, selections) {
if (selections.length === 1) {
var node = this.getNode(selections[0]);
Ext.fly(node).scrollIntoView(this.el);
}
}
},
...
}

It isn't directly related to extJs, but you could get the DOM element you want in your tree and use scrollTo to get to it.

var path = tree.getSelectionModel().getSelectedNode().getPath('id');
//reload data,
tree.getLoader().load(tree.getRootNode(),function(treeNode){
//expand path and select node
tree.expandPath(path,'id',function(bSucess,oLastNode){
tree.getSelectionModel().select(oLastNode);
});
},this);

Related

binding dynamic dom elements to content.js plugin

I have a plugin that im making use of called content.js http://innovastudio.com/content-builder.aspx
Im adding in dynamic divs to the page which I would like to have the content.js plugin assigned to it, so I can make use of its functionality.
On a single div, or already defined div within the page, I dont appear to have any issues with multiple divs.
However if I add in a div with the same class, I cant seem to bind the plugin to it.
Ive included the code for instantiating the div with the contentbuilder plugin, but I wondering if there is a way to bind it to new elements that are added to the page with the class of "letter". Or if there is a generic way of binding plugins to divs using jquery.
$('div.letter').contentbuilder({
enableZoom:false,
snippetOpen: true,
imageselect: 'images.html',
fileselect: 'images.html',
snippetFile: '/assets/templates/content-builder/default/snippets.html',
toolbar: 'left',
//sourceEditor: false,
onDrop:function(){
// function for when an item is dragged into the editable area
},
onRender: function () {
var coverLength = $("#coverpage div.row").length;
var mainContent = $("#maincontent div.row").length;
if(coverLength == 0)
{
$("#coverpage").html('<div class="no-content-on-page">Select your content from the right sidebar</div>')
}
else
{
$("#coverpage div.no-content-on-page").remove();
}
if(mainContent == 0)
{
$("#maincontent").html('<div class="no-content-on-page">Select your content from the right sidebar</div>')
}
else
{
$("#maincontent div.no-content-on-page").remove();
}
//custom script here
}
});
If you must add these divs in a dinamic way, i think that you should init the plugin for each time that you add a new div. To avoid init same div twice, use some class like in the following example:
function createLetter(){
$("body").append('<div class="letter mustBeActivated"></div>');
initContentBuilder();
}
function initContentBuilder(){
$('div.letter.mustBeActivated').contentbuilder({
enableZoom:false,
snippetOpen: true,
imageselect: 'images.html',
fileselect: 'images.html',
snippetFile: '/assets/templates/content-builder/default/snippets.html',
toolbar: 'left',
//sourceEditor: false,
onDrop:function(){
// function for when an item is dragged into the editable area
},
onRender: function () {
var coverLength = $("#coverpage div.row").length;
var mainContent = $("#maincontent div.row").length;
if(coverLength == 0)
{
$("#coverpage").html('<div class="no-content-on-page">Select your content from the right sidebar</div>')
}
else
{
$("#coverpage div.no-content-on-page").remove();
}
if(mainContent == 0)
{
$("#maincontent").html('<div class="no-content-on-page">Select your content from the right sidebar</div>')
}
else
{
$("#maincontent div.no-content-on-page").remove();
}
//custom script here
}
}).removeClass('mustBeActivated');
}

How to implement a delegator using ExtJS 4.1

I am new to javascript, but I've been hired to give maintenance to an application which is developed in Sencha ExtJS 4. One of the modules I've been asked to modify, is of a component in which I show a tooltip whenever I hover over it. This component can be present in more than one view, it is something like "Customer Details" that is present in many screens of the application. If I hover over this data, I need to show a tooltip, this tooltip shows information retrieved by server (REST). I implemented some logic, but this logic involves the use of many listeners in each of the components that will show the information. For instance, I added a listener in all of the views that requires showing the tooltip:
this.listeners = {
boxready: {
fn: this.onAfterRender,
scope: this
}
And I had to implement this method for every view as well, which is a mess and, for sure, a very bad practice:
/**
* This method is executed after panels are rendered in order to set ToolTip listeners on
* users and workgroups.
*
* #param {Object} scope
*/
onAfterRender: function(scope) {
Ext.defer(function() {
var usElements = Ext.get(Ext.query('.usertooltip', scope.el.dom));
usElements.on({
click: function (e) {
var item = Ext.get(e.target);
if (Ext.isEmpty(item.dom.innerHTML.trim())) {
item.removeCls('usertooltip');
return;
}
if (item.hasCls('usertooltip-clicked')) {
return;
}
item.addCls('usertooltip-clicked');
var user = item.getAttribute('data-info');
UserInfo.getUserInfo(user, false);
if (UserInfo.errorResponse) {
UserInfo.getWGroupInfo(user);
}
UserInfo.displayToolTip(this);
}
});
var wgElements = Ext.get(Ext.query('.wgtooltip', scope.el.dom));
wgElements.on({
click : function (e) {
var item = Ext.get(e.target);
if (Ext.isEmpty(item.dom.innerHTML.trim())) {
item.removeCls('wgtooltip');
return;
}
if (item.hasCls('wgtooltip-clicked')) {
return;
}
item.addCls('wgtooltip-clicked');
var wgroup = item.getattribute('data-info');
WGroupInfo.getWGroupInfo(wgroup, false);
if (UserInfo.errorResponse) {
WGroupInfo.getUserInfo(wgroup);
}
WGroupInfo.displayToolTip(this);
}
});
}, 1000, this);
},
What I do is simply detect if the item is selected based a css class, if so, I handle the events and proceed with logic. But I've been doing some research and I think this can be achieved using a "delegator" but I am not sure how to implement this for my scenario.
What I've been thinking of, so far is to create a "js" class which have a method like an "observer" and whenever listen to someone asking for this tooltip functionality, delegate it to the executing object. But since I am new to javascript and this Sencha ExtJS, my tries have been frustrated. If someone can help me I would really appreciate it.
Thanks in advance.
Best regards.
The best way would be to declare a plugin:
Ext.define('TipPlugin', {
alias: 'plugin.tip',
init: function(c) {
c.on('boxready', this.onBoxReady, this);
},
onBoxReady: function(c) {
var els = this.el.select('.usertooltip');
// Do stuff!
}
});
var c = new Ext.Component({
plugins: ['tip']
});

jQuery "on create" event for dynamically-created elements

I need to be able to dynamically create <select> element and turn it into jQuery .combobox(). This should be element creation event, as opposed to some "click" event in which case I could just use jQuery .on().
So does something like this exist?
$(document).on("create", "select", function() {
$(this).combobox();
}
I'm reluctant to use livequery, because it's very outdated.
UPDATE The mentioned select/combobox is loaded via ajax into a jQuery colorbox (modal window), thus the problem - I can only initiate combobox using colorbox onComplete, however on change of one combobox another select/combobox must be dynamically created, therefor I need a more generic way to detect creation of an element (selectin this case).
UPDATE2 To try and explain the problem further - I have select/combobox elements created recursively, there is also a lot of initiating code inside .combobox(), therefore if I used a classic approach, like in #bipen's answer, my code would inflate to insane levels. Hope this explains the problem better.
UPDATE3 Thanks everyone, I now understand that since deprecation of DOMNodeInserted there is a void left in DOM mutation and there is no solution to this problem. I'll just have to rethink my application.
You can on the DOMNodeInserted event to get an event for when it's added to the document by your code.
$('body').on('DOMNodeInserted', 'select', function () {
//$(this).combobox();
});
$('<select>').appendTo('body');
$('<select>').appendTo('body');
Fiddled here: http://jsfiddle.net/Codesleuth/qLAB2/3/
EDIT: after reading around I just need to double check DOMNodeInserted won't cause problems across browsers. This question from 2010 suggests IE doesn't support the event, so test it if you can.
See here: [link] Warning! the DOMNodeInserted event type is defined in this specification for reference and completeness, but this specification deprecates the use of this event type.
As mentioned in several other answers, mutation events have been deprecated, so you should use MutationObserver instead. Since nobody has given any details on that yet, here it goes...
Basic JavaScript API
The API for MutationObserver is fairly simple. It's not quite as simple as the mutation events, but it's still okay.
function callback(records) {
records.forEach(function (record) {
var list = record.addedNodes;
var i = list.length - 1;
for ( ; i > -1; i-- ) {
if (list[i].nodeName === 'SELECT') {
// Insert code here...
console.log(list[i]);
}
}
});
}
var observer = new MutationObserver(callback);
var targetNode = document.body;
observer.observe(targetNode, { childList: true, subtree: true });
<script>
// For testing
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
Let's break that down.
var observer = new MutationObserver(callback);
This creates the observer. The observer isn't watching anything yet; this is just where the event listener gets attached.
observer.observe(targetNode, { childList: true, subtree: true });
This makes the observer start up. The first argument is the node that the observer will watch for changes on. The second argument is the options for what to watch for.
childList means I want to watch for child elements being added or removed.
subtree is a modifier that extends childList to watch for changes anywhere in this element's subtree (otherwise, it would just look at changes directly within targetNode).
The other two main options besides childList are attributes and characterData, which mean about what they sound like. You must use one of those three.
function callback(records) {
records.forEach(function (record) {
Things get a little tricky inside the callback. The callback receives an array of MutationRecords. Each MutationRecord can describe several changes of one type (childList, attributes, or characterData). Since I only told the observer to watch for childList, I won't bother checking the type.
var list = record.addedNodes;
Right here I grab a NodeList of all the child nodes that were added. This will be empty for all the records where nodes aren't added (and there may be many such records).
From there on, I loop through the added nodes and find any that are <select> elements.
Nothing really complex here.
jQuery
...but you asked for jQuery. Fine.
(function($) {
var observers = [];
$.event.special.domNodeInserted = {
setup: function setup(data, namespaces) {
var observer = new MutationObserver(checkObservers);
observers.push([this, observer, []]);
},
teardown: function teardown(namespaces) {
var obs = getObserverData(this);
obs[1].disconnect();
observers = $.grep(observers, function(item) {
return item !== obs;
});
},
remove: function remove(handleObj) {
var obs = getObserverData(this);
obs[2] = obs[2].filter(function(event) {
return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
});
},
add: function add(handleObj) {
var obs = getObserverData(this);
var opts = $.extend({}, {
childList: true,
subtree: true
}, handleObj.data);
obs[1].observe(this, opts);
obs[2].push([handleObj.selector, handleObj.handler]);
}
};
function getObserverData(element) {
var $el = $(element);
return $.grep(observers, function(item) {
return $el.is(item[0]);
})[0];
}
function checkObservers(records, observer) {
var obs = $.grep(observers, function(item) {
return item[1] === observer;
})[0];
var triggers = obs[2];
var changes = [];
records.forEach(function(record) {
if (record.type === 'attributes') {
if (changes.indexOf(record.target) === -1) {
changes.push(record.target);
}
return;
}
$(record.addedNodes).toArray().forEach(function(el) {
if (changes.indexOf(el) === -1) {
changes.push(el);
}
})
});
triggers.forEach(function checkTrigger(item) {
changes.forEach(function(el) {
var $el = $(el);
if ($el.is(item[0])) {
$el.trigger('domNodeInserted');
}
});
});
}
})(jQuery);
This creates a new event called domNodeInserted, using the jQuery special events API. You can use it like so:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
I would personally suggest looking for a class because some libraries will create select elements for testing purposes.
Naturally, you can also use .off("domNodeInserted", ...) or fine-tune the watching by passing in data like this:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
This would trigger checking for the appearance of a select.test element whenever attributes changed for elements directly inside the body.
You can see it live below or on jsFiddle.
(function($) {
$(document).on("domNodeInserted", "select", function() {
console.log(this);
//$(this).combobox();
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
// For testing
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
<script>
(function($) {
var observers = [];
$.event.special.domNodeInserted = {
setup: function setup(data, namespaces) {
var observer = new MutationObserver(checkObservers);
observers.push([this, observer, []]);
},
teardown: function teardown(namespaces) {
var obs = getObserverData(this);
obs[1].disconnect();
observers = $.grep(observers, function(item) {
return item !== obs;
});
},
remove: function remove(handleObj) {
var obs = getObserverData(this);
obs[2] = obs[2].filter(function(event) {
return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
});
},
add: function add(handleObj) {
var obs = getObserverData(this);
var opts = $.extend({}, {
childList: true,
subtree: true
}, handleObj.data);
obs[1].observe(this, opts);
obs[2].push([handleObj.selector, handleObj.handler]);
}
};
function getObserverData(element) {
var $el = $(element);
return $.grep(observers, function(item) {
return $el.is(item[0]);
})[0];
}
function checkObservers(records, observer) {
var obs = $.grep(observers, function(item) {
return item[1] === observer;
})[0];
var triggers = obs[2];
var changes = [];
records.forEach(function(record) {
if (record.type === 'attributes') {
if (changes.indexOf(record.target) === -1) {
changes.push(record.target);
}
return;
}
$(record.addedNodes).toArray().forEach(function(el) {
if (changes.indexOf(el) === -1) {
changes.push(el);
}
})
});
triggers.forEach(function checkTrigger(item) {
changes.forEach(function(el) {
var $el = $(el);
if ($el.is(item[0])) {
$el.trigger('domNodeInserted');
}
});
});
}
})(jQuery);
</script>
Note
This jQuery code is a fairly basic implementation. It does not trigger in cases where modifications elsewhere make your selector valid.
For example, suppose your selector is .test select and the document already has a <select>. Adding the class test to <body> will make the selector valid, but because I only check record.target and record.addedNodes, the event would not fire. The change has to happen to the element you wish to select itself.
This could be avoided by querying for the selector whenever mutations happen. I chose not to do that to avoid causing duplicate events for elements that had already been handled. Properly dealing with adjacent or general sibling combinators would make things even trickier.
For a more comprehensive solution, see https://github.com/pie6k/jquery.initialize, as mentioned in Damien Ó Ceallaigh's answer. However, the author of that library has announced that the library is old and suggests that you shouldn't use jQuery for this.
You can use DOMNodeInserted mutation event (no need delegation):
$('body').on('DOMNodeInserted', function(e) {
var target = e.target; //inserted element;
});
EDIT: Mutation events are deprecated, use mutation observer instead
Just came up with this solution that seems to solve all my ajax problems.
For on ready events I now use this:
function loaded(selector, callback){
//trigger after page load.
$(function () {
callback($(selector));
});
//trigger after page update eg ajax event or jquery insert.
$(document).on('DOMNodeInserted', selector, function () {
callback($(this));
});
}
loaded('.foo', function(el){
//some action
el.css('background', 'black');
});
And for normal trigger events I now use this:
$(document).on('click', '.foo', function () {
//some action
$(this).css('background', 'pink');
});
There is a plugin, adampietrasiak/jquery.initialize, which is based on MutationObserver that achieves this simply.
$.initialize(".some-element", function() {
$(this).css("color", "blue");
});
This could be done with DOM4 MutationObservers but will only work in Firefox 14+/Chrome 18+ (for now).
However there is an "epic hack" (author's words not mine!) that works in all browsers that support CSS3 animations which are: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. See the demo from the blog. The hack is to use a CSS3 animation event with a given name that is observed and acted upon in JavaScript.
One way, which seems reliable (though tested only in Firefox and Chrome) is to use JavaScript to listen for the animationend (or its camelCased, and prefixed, sibling animationEnd) event, and apply a short-lived (in the demo 0.01 second) animation to the element-type you plan to add. This, of course, is not an onCreate event, but approximates (in compliant browsers) an onInsertion type of event; the following is a proof-of-concept:
$(document).on('webkitAnimationEnd animationend MSAnimationEnd oanimationend', function(e){
var eTarget = e.target;
console.log(eTarget.tagName.toLowerCase() + ' added to ' + eTarget.parentNode.tagName.toLowerCase());
$(eTarget).draggable(); // or whatever other method you'd prefer
});
With the following HTML:
<div class="wrapper">
<button class="add">add a div element</button>
</div>
And (abbreviated, prefixed-versions-removed though present in the Fiddle, below) CSS:
/* vendor-prefixed alternatives removed for brevity */
#keyframes added {
0% {
color: #fff;
}
}
div {
color: #000;
/* vendor-prefixed properties removed for brevity */
animation: added 0.01s linear;
animation-iteration-count: 1;
}
JS Fiddle demo.
Obviously the CSS can be adjusted to suit the placement of the relevant elements, as well as the selector used in the jQuery (it should really be as close to the point of insertion as possible).
Documentation of the event-names:
Mozilla | animationend
Microsoft | MSAnimationEnd
Opera | oanimationend
Webkit | webkitAnimationEnd
W3C | animationend
References:
caniuse.com summary of compatibility of CSS Animations.
CSS AnimationEvent Interface (W3C).
JavaScript animationend vendor-support.
For me binding to the body does not work. Binding to the document using jQuery.bind() does.
$(document).bind('DOMNodeInserted',function(e){
var target = e.target;
});
instead of...
$(".class").click( function() {
// do something
});
You can write...
$('body').on('click', '.class', function() {
// do something
});
I Think it's worth mentioning that in some cases, this would work:
$( document ).ajaxComplete(function() {
// Do Stuff
});
create a <select> with id , append it to document.. and call .combobox
var dynamicScript='<select id="selectid"><option value="1">...</option>.....</select>'
$('body').append(dynamicScript); //append this to the place your wanted.
$('#selectid').combobox(); //get the id and add .combobox();
this should do the trick.. you can hide the select if you want and after .combobox show it..or else use find..
$(document).find('select').combobox() //though this is not good performancewise
if you are using angularjs you can write your own directive. I had the same problem whith bootstrapSwitch. I have to call
$("[name='my-checkbox']").bootstrapSwitch();
in javascript but my html input object was not created at that time. So I write an own directive and create the input element with
<input type="checkbox" checkbox-switch>
In the directive I compile the element to get access via javascript an execute the jquery command (like your .combobox() command). Very important is to remove the attribute. Otherwise this directive will call itself and you have build a loop
app.directive("checkboxSwitch", function($compile) {
return {
link: function($scope, element) {
var input = element[0];
input.removeAttribute("checkbox-switch");
var inputCompiled = $compile(input)($scope.$parent);
inputCompiled.bootstrapSwitch();
}
}
});

Best way to silently bind window resize event to jQuery plugin without keeping a reference to the targeted element

I'm looking for best-practice advice.
I'm writing a small jQuery plugin to manage horizontal scroll on elements.
I need all the dom elements targeted by that plugin to update on window resize.
Fact is, my website is a full ajax 'app' so when I remove DOM elements, I need them gone so memory doesn't leak.
But I can't find a way to bind the resize event without keeping a reference to the DOM node.
EDIT :
Actually I need the resize handler to get the plugin-targeted elements at 'call' time, coz I don't want to keep any reference to those elements in memory, because I might call .html('') on a parent of theirs...
I did not paste all my code, just an empty shell. I already have a destroy method that unbinds handlers. But I'm generating, removing and appending html nodes dynamically and I the the elements targeted by the plugin to remove silently.
Kevin B stated I could override jQuery .remove method to deal with the handlers, but would have to load jQuery UI for it to work. I don't want that either..
Here is what I tried (attempts commented):
(function($) {
// SOLUTION 2 (see below too)
// Not good either coz elements are not removed until resize is triggered
/*
var hScrolls = $([]);
$(window).bind('resize.hScroll',function(){
if(!hScrolls.length) return;
hScrolls.each(function(){
if($(this).data('hScroll')) $(this).hScroll('updateDimensions');
else hScrolls = hScrolls.not($(this));
});
});
*/
// END SOLUTION 2
// SOLUTION 3 (not implemented but I think I'm on the right path)
$(window).bind('resize.hScroll',function(){
// need to get hScroll'ed elements via selector...
$('[data-hScroll]').hScroll('updateDimensions');
// I don't know how....
});
// END SOLUTION 3
var methods = {
init : function(options) {
var settings = $.extend( {
defaults: true
}, options);
return this.each(function() {
var $this = $(this),
data = $this.data('hScroll');
if (!data) {
$this.data('hScroll', {
target: $this
});
// SOLUTION 1
// This is not good: it keeps a reference to $this when I remove it...
/*
$(window).bind('resize.hScroll', function(){
$this.hScroll('updateDimensions');
});
*/
// END SOLUTION 1
$this.hScroll('updateDimensions');
// SOLUTION 2 (see above too)
hScrolls = hScrolls.add(this);
}
});
},
updateDimensions: function(){
var hScroll = this.data('hScroll');
// do stuff with hScroll.target
}
}
$.fn.hScroll = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if ( typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.hScroll');
}
};
})(jQuery);​
Thanks all in advance!
jQuery calls cleanData any time you do something that removes or replaces elements (yes, even if you use parent.html("") ). You can take advantage of that by extending it and having it trigger an event on the target elements.
// This is taken from https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 10/17/2012
if (!$.widget) { // prevent duplicating if jQuery ui widget is already included
var _cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
try {
$( elem ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
}
_cleanData( elems );
};
}
Now you can bind to the remove event when setting up your plugin and have it run your destroy method.
$(elem).bind("remove",methods.destroy)
You might use a class name and forward the resize event:
$.fn.hScroll = function(method) {
this
.addClass('hScroll')
.data('method', arguments)
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$(".hScroll").each(function(){
var method_config = $(this).data('method');
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
// Would alert "hey you" for <a class="test">you</a> on every resize
Update
If you change the dom and want to keep the selector you might try this one:
var elements = [];
$.fn.hScroll = function(method) {
elements.push({'selector' : this.selector, 'arguments' : arguments });
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$.each(elements,function(i, element){
$(element.selector).each(function(){
var method_config = element.arguments;
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
$(document.body).html("<a class='test'>you</a>");
// Would alert "hey you" for every window resize
You should have the scroll event bound in the extension. Also, you will want to add a "destroy" method to your extension as well. Before you remove the element from the DOM, you will want to call this method. Inside the detroy method is where you will want to unbind the resize event.
One important thing in making this work is that you have a reference to each handler method that is bound to the resize event. Alternatively, you can unbind All resize events upon the removal on an element and then rebind the scroll event to the remaining elements that require it.

why is my element not targeted when reload through AJAX

I'm using object literals on my project. I'm targeting selecting with jquery. It works fine the first time but when the part I'm targeting is reloaded with AJAX I can't target those element anymore. But I look into firebug they're there...
I'm even doing console.log() to test if my code works and it works but it just doesn't want to pick those. So in order for it to work, I have to refresh the entire browser.
Do you know what's the deal with AJAX dom reload and selectors.
I think it's something to do with the DOM reloading and redrawing itself or something along those lines...
Here is my code:
Module.editWishlistTitle = {
wishListContent: $('.mod-wish-list-content'),
title: $('.title').find('h2'),
titleTextField: $('#wishlist-title-field'),
titleInnerContainer: $('.title-inner'),
editTitleForm: $('.edit-title-form'),
submitCancelContainer: $('.submit-cancel'),
notIE9: $.browser.msie && $.browser.version < 9,
edit: function () {
var fieldTxt = this.titleTextField.val(),
editForm = this.editTitleForm,
titleParent = this.titleInnerContainer,
fieldCurrentTitle = this.title.text();
this.titleTextField.val(fieldCurrentTitle);
this.submitCancelContainer.removeClass('hidden');
if (!this.notIE9) {
editForm.css('opacity', 0).animate({ opacity: 1 }).removeClass('hidden');
titleParent.addClass('hidden').animate({ opacity: 0 });
console.log(editForm);
} else {
editForm.removeClass('hidden');
titleParent.addClass('hidden');
}
}
init: function () {
var self = this;
console.log(this.editTitleForm);
//edit
this.wishListContent.delegate('.edit-title a', 'click', function (e) {
self.edit();
e.preventDefault();
});
};
If you are replacing an element on the page, you are destroying the original reference to the element. You need to redo the reference to point to the new element.
Create a new method in your code that (re)initializes the references you need. Instead of adding them in the odject, set them in the method.
Basic idea:
Module.editWishlistTitle = {
wishListContent: $('.mod-wish-list-content'),
title: $('.title').find('h2'),
//titleTextField: $('#wishlist-title-field'),
...
...
initReferences : function(){
this.titleTextField = $('#wishlist-title-field');
},
...
...
init: function () {
this.initReferences();
...
...
And when your Ajax call comes back you just need to call initReferences again.
After DOM ready, if you inject any data / class / id will not be available in DOM, so better you use live or delegate to get your new data access.
http://api.jquery.com/delegate/
Best to use delegate, that will take care your new data loaded after dom ready, that way you can avoid to refresh /reload your page.

Categories