$(this) in JavaScript Module Pattern - javascript

I'm trying to use the Javascript module pattern for the first time to organize my code. I understand the how "this" works here but cannot figure out how $(this) works. For example, the code below,
$(this).addClass('video-active'); in "chooseVideo" function, I want to apply addClass only for the clicked element. But it does not work. Could anybody give me some advice? Thank you!
;(function() {
'use strict';
if (typeof window.jQuery !== 'function') {
return;
}
var $ = jQuery;
var video = {
init: function() {
this.cacheDom();
this.bindEvents();
this.render();
},
cacheDom: function() {
this.$el = $('#videoWrap');
this.$button = this.$el.find('.video');
},
render: function() {
},
bindEvents: function() {
this.$button.bind('click', this.chooseVideo.bind(this));
},
chooseVideo: function(e) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
this.$button.removeClass('video-active');
$(this).addClass('video-active');
}
};
video.init();
})();

when you use bind, you are changing the context of this
So you will need to use the event object to get what was clicked.
chooseVideo: function(e) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
this.$button.removeClass('video-active');
$(e.target).addClass('video-active');

Related

javascript module pattern: initialise two objects on load

I have two objects defined, each one has an init function as follows
var TestSound = {
settings: {
testSoundFile : "static/main/persoonS",
postUrl : "dummy.com",
},
init : function(){
this.cacheDom();
this.bindEvents();
},
cacheDom: function(){
this.oneCertainButton = document.getElementById("buttonId");
this.soundElement = document.getElementById("sound");
},
bindEvents : function(){
that = this;
this.oneCertainButton.onclick = function(){ that.playSound(that.settings.testSoundFile); };
},
............
var CheckInput = {
settings: {
correctAnswer : "persoon",
},
init : function (){
this.cacheDom();
this.bindEvents();
},
cacheDom: function (){
this.submitButton = document.getElementById("submitButtonId");
this.answerInputBox = document.getElementById("answerId");
this.feedbackField = document.getElementById("feedbackId");
},
bindEvents: function(){
that = this;
this.submitButton.addEventListener("click", function(event){
..........
I wish to initialise them both (one after the other) with "window onload" like so:
window.onload = function() {
CheckInput.init.bind(CheckInput);
TestSound.init.bind(TestSound);
};
This doesn't work apparently as the init functions do not seem to be called.
However, it works if I initialise just one of the modules like so:
window.onload =
CheckInput.init.bind(CheckInput);
Any explanations on what is going wrong here much appreciated!
You want either
window.onload = function() {
CheckInput.init();
TestSound.init();
};
or
window.addEventListener("load", CheckInput.init.bind(CheckInput));
window.addEventListener("load", TestSound.init.bind(TestSound));
Any explanations on what is going wrong here much appreciated!
You have to create a function to be used as a handler that is called when the event fires. You can do that by using bind or with a function expression, but if you use both then only another function will be created when the event happens, and the init method is not called at all.

Calling function inside javascript class

I have the following javascript class (really don't know if it is the best solution) and I wan't to call a function that is definded in this class, insinde another function defined in the same class.
step1 = {
init: function(){
this.events();
},
events: function(){
fct = this; // i stored this in a variable so that i don't lose it
$(document).on('click', '.form-line li', function(e) {
e.preventDefault();
fct.changeCategoryValue($(this));
});
$(document).on('click', '.alert-form .confirm', function(e) {
e.preventDefault();
$.fancybox.close(true);
});
},
changeCategoryValue: function(el) {
cat = el.hasClass('has-sub') ? '' : el.data('id');
title = el.hasClass('has-sub') ? '' : el.data('title');
$('#cat-title').val(title);
$("input[name='category']").val(cat);
}
As you an see I wan't to call the changeCategoryValue function but if I call it with this.changeCategoryValue it won't work. Any suggestions on how to improve the code?
Alternatively, you may change the scope of the function callback:
$(document).on('click', '.form-line li', function(e) {
e.preventDefault();
this.changeCategoryValue($(e.currentTarget));
}.bind(this));
Use .bind(this) so the scope of function(e){...} will be the instance of step1 instead of $('.form-line li',document). Now, to still target the clicked/selected .form-line li, you can access the object via e.currentTarget.
try to use:
step1.changeCategoryValue($(this));
you have added property to the class why don't you call it using class name like this :
step1.changeCategoryValue('any_element_ref');
You could create a proper object. This even allows you call init in the constructor if you wan't.
function Step() { this.init(); }
Step.prototype.init = function() { this.events(); };
Step.prototype.events = function() {
var self = this;
$(document).on('click', '.form-line li', function(e) {
e.preventDefault();
self.changeCategoryValue($(this));
});
$(document).on('click', '.alert-form .confirm', function(e) {
e.preventDefault();
$.fancybox.close(true);
});
};
Step.prototype.changeCategoryValue = function(el) {
cat = el.hasClass('has-sub') ? '' : el.data('id');
title = el.hasClass('has-sub') ? '' : el.data('title');
$('#cat-title').val(title);
$("input[name='category']").val(cat);
};
var step1 = new Step();
you can try use clojure like this:
step1 = (function(){
function events(){
$(document).on('click', '.form-line li', function(e) {
e.preventDefault();
changeCategoryValue($(this));
});
$(document).on('click', '.alert-form .confirm', function(e) {
e.preventDefault();
$.fancybox.close(true);
});
}
function changeCategoryValue(el) {
cat = el.hasClass('has-sub') ? '' : el.data('id');
title = el.hasClass('has-sub') ? '' : el.data('title');
$('#cat-title').val(title);
$("input[name='category']").val(cat);
}
function init(){
events();
}
return {
init:init,
changeCategoryValue: changeCategoryValue
}
})()

Backbone events not firing after on demand loading element

I'm using backbone and lazy loading views in a single page application as I need them. However, it appears doing this seems to be confusing the way backbone knows what my 'el' is when setting up events. Using the view definition below, I'm trying to get the code that fires on the submit button click or the input fields changing but right now, neither appear to work.
$(document).ready(function () {
editaddressView = Backbone.View.extend({
elementReady: false,
initialize: function () {
this.model = window.AccountData;
this.listenTo(this.model, "change", this.render);
if ($('#section-editaddress').length == 0) {
// Load UI
$('#ajax-sections').prepend('<div class="section" id="section-editaddress" style="display: none;"></div>');
}
this.el = $('#section-editaddress');
},
events: {
"click #edit-address-submit": "beginSaving",
"change input": "updateModel",
"change select": "updateModel"
},
render: function () {
$(this.el).find("[name=address]").val(this.model.get('owner_address1'));
// ...
return this;
},
switchTo: function () {
// Set menu state
$('.js-NavItem').removeClass('active');
$('#sN-li-personal').addClass('active');
if (this.options.isPreLoaded)
this.elementReady = true;
if (this.elementReady) {
this.renderSwitch();
}
else {
var model = this;
$('#section-editaddress').load('/ajax/ui/editaddress', function (response, status, xhr) {
if (status == "error") {
$('#page-progress-container').fadeOut('fast', function () {
$('#page-load-error').fadeIn('fast');
});
} else {
$('#section-editaddress').find('.routedLink').click(function (e) {
window.Router.navigate($(this).attr('href'), true);
return false;
});
model.delegateEvents();
model.elementReady = true;
model.render(); // First render
model.renderSwitch();
}
});
}
},
renderSwitch: function () {
// Abort showing loading progress if possible
if (window.firstRunComplete) {
clearTimeout(window.pageHide);
// Change screen - Fade progress if needed
$('#page-progress-container').fadeOut('fast', function () {
$('#page-load-error').fadeOut('fast');
var sections = $(".section");
var numSections = sections.length;
var i = 0;
sections.hide('drop', { easing: 'easeInCubic', direction: 'left' }, 350, function () {
i++;
if (i == numSections) {
$('#section-editaddress').show('drop', { easing: 'easeInExpo', direction: 'right' }, 350).removeClass('hidden');
$.scrollTo($('#contentRegion'), 250, { margin: true });
}
});
});
}
// Switch complete
window.changingPage = false;
},
updateModel: function () {
var changedItems = {};
if (this.model.get('csrf') != $(this.el).find("[name=csrf]").val())
changedItems.csrf = $(this.el).find("[name=csrf]").val();
// ...
},
beginSaving: function () {
alert('test');
}
});
});
Can anyone see what I've missed?
Whenever you need to change or modify the DOM element of a BackboneJS view manually, you should use setElement rather than setting the property directly. It moves all of the event handlers to the newly attached DOM element and also sets the $el property. In addition, the function also detaches any existing event handlers.
So, in the code you pasted, you'd just change it to:
this.setElement($('#section-editaddress'));

Get event inside of EventListener

The following code doesn't work:
slider.prototype.onTransitionEnd = function() {
var self = this;
self.slideElement.addEventListener(self.startEvent, function() { self.onTouchStart(/* e, event or what? */ }, false);
}
slider.prototype.onTouchStart = function(e) {
alert(e);
//HERE I NEED THE EVENT, HOW CAN I GET THIS?
}
The problem is, that I cannot access to the event inside the function onTouchStart.
By default, the event automatically transferred:
function onTransitionEnd() {
slide.addEventListener(startEvent, onTouchStart, false);
}
function onTouchStart(e) {
alert(e) /* IT WORK'S! */
}
But how does it work in my example?
You seem to be missing a parenthesis. Could that be what's wrong?
slider.prototype.onTransitionEnd = function() {
var self = this;
self.slideElement.addEventListener(self.startEvent, function() { self.onTouchStart(/* e, event or what? */) }, false);
}
slider.prototype.onTouchStart = function(e) {
alert(e);
//HERE I NEED THE EVENT, HOW CAN I GET THIS?
}

Create a jQuery special event for content changed

I'm trying to create a jQuery special event that triggers when the content that is bound, changes. My method is checking the content with a setInterval and check if the content has changed from last time. If you have any better method of doing that, let me know. Another problem is that I can't seem to clear the interval. Anyway, what I need is the best way to check for content changes with the event.special.
(function(){
var interval;
jQuery.event.special.contentchange = {
setup: function(data, namespaces) {
var $this = $(this);
var $originalContent = $this.text();
interval = setInterval(function(){
if($originalContent != $this.text()) {
console.log('content changed');
$originalContent = $this.text();
jQuery.event.special.contentchange.handler();
}
},500);
},
teardown: function(namespaces){
clearInterval(interval);
},
handler: function(namespaces) {
jQuery.event.handle.apply(this, arguments)
}
};
})();
And bind it like this:
$('#container').bind('contentchange', function() {
console.log('contentchange triggered');
});
I get the console.log 'content changed', but not the console.log 'contentchange triggered'. So it's obvious that the callback is never triggered.
I just use Firebug to change the content and to trigger the event, to test it out.
Update
I don't think I made this clear enough, my code doesn't actually work. I'm looking for what I'm doing wrong.
Here is the finished code for anyone interested
(function(){
var interval;
jQuery.event.special.contentchange = {
setup: function(){
var self = this,
$this = $(this),
$originalContent = $this.text();
interval = setInterval(function(){
if($originalContent != $this.text()) {
$originalContent = $this.text();
jQuery.event.handle.call(self, {type:'contentchange'});
}
},100);
},
teardown: function(){
clearInterval(interval);
}
};
})();
Thanks to Mushex for helping me out.
also take a look to James similar script (declaring as jquery object method and not as event)
jQuery.fn.watch = function( id, fn ) {
return this.each(function(){
var self = this;
var oldVal = self[id];
$(self).data(
'watch_timer',
setInterval(function(){
if (self[id] !== oldVal) {
fn.call(self, id, oldVal, self[id]);
oldVal = self[id];
}
}, 100)
);
});
return self;
};
jQuery.fn.unwatch = function( id ) {
return this.each(function(){
clearInterval( $(this).data('watch_timer') );
});
};
and creating special event
jQuery.fn.valuechange = function(fn) {
return this.bind('valuechange', fn);
};
jQuery.event.special.valuechange = {
setup: function() {
jQuery(this).watch('value', function(){
jQuery.event.handle.call(this, {type:'valuechange'});
});
},
teardown: function() {
jQuery(this).unwatch('value');
}
};
Anyway, if you need it only as event, you script is nice :)
I know this post/question is a little old, but these days I was behind a similar solution and I found this:
$('#selector').bind('DOMNodeInserted', function(e) {
console.log(e.target);
});
Source: http://naspinski.net/post/Monitoring-a-DOM-Element-for-Modification-with-jQuery.aspx
Hope this help someone!
The finished code in the original question worked for me, thank you! I would just like to note that I am using jquery 1.9.1 and $.event.handle seems to have been removed. I changed the following to get it to work.
jQuery.event.handle.call(self, {type:'contentchange'});
to
jQuery.event.dispatch.call(self, {type:'contentchange'});
maybe you could try Mutation Observer
Here are the code:
mainArea = document.querySelector("#main_area");
MutationObserver = window.MutationObserver;
DocumentObserver = new MutationObserver(function() {
//what you want to run
});
DocumentObserverConfig = {attributes: true, childList: true, characterData: true, subtree: true};
DocumentObserver.observe(mainArea, DocumentObserverConfig);

Categories

Resources