Self-Invoking Function Scope with Dynamically Binded Content - javascript

I have wrapped my JavaScript in a self-invoking function to keep everything contained. Hoever, I have run into an issue where my links, which are dynamically built on the fly, return an error "Function not defined" when they are clicked. Let me include my relevant code:
(function(window, document, $) {
function buildChapterList() {
for(var i=0; i<Object.keys(theChapterCues).length; i++) {
chapterListHTML = "<li><a class='fun-blue' href='javascript:void(0)' onClick=\"skipToChapter('"+ i +"')\">"+ theChapterCues[i]["title"] +"</a></li>";
$(chapterListHTML).appendTo("ul#chapter-list");
$("body").bind("DOMNodeInserted", function() {
$(this).find('#chapter-list li').first().addClass("active");
});
}
}
function skipToChapter(theChapter) {
if (theChapter == 0) {
theVideo.currentTime=0;
} else {
var thisChapterStart = parseInt(cues[theChapter]["chapterStart"]+1);
theVideo.currentTime=thisChapterStart/frameRate;
}
}
}(this, this.document, this.jQuery));
When clicking on one of the generated links I receive the following error:
Uncaught ReferenceError: skipToChapter is not defined
Am I missing something on scope? Is this a binding issue? Any advice would be greatly appreciated. Thanks!

The skipToChapter function is not in the global scope, so can't be called from an inline click handler. Instead you should assign the click handler when you build the link, in an unobtrusive way like below. If you assign it like this, then skipToChapter is in scope, and you don't have to make it a global, and you remove the need for undesirable inline event handlers.
function buildChapterList() {
for(var i=0; i<Object.keys(theChapterCues).length; i++) {
chapterListHTML = $("<li><a class='fun-blue' href='javascript:void(0)'>"+ theChapterCues[i]["title"] +"</a></li>");
(function(i){
chapterListHTML.find('a').click(skipToChapter.bind(this, i));
})(i);
chapterListHTML.appendTo("ul#chapter-list");
$("body").bind("DOMNodeInserted", function() {
$(this).find('#chapter-list li').first().addClass("active");
});
}
}

skipToChapter function is only visible within the outer anonymous function. You can read about Javascript scope here: http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/
The quick and dirty way of solving the problem would be defining skipToChapter outside of the anonymous function or as a member of the window object.
For example:
window.skipToChapter = function(theChapter) {
if (theChapter == 0) {
theVideo.currentTime=0;
} else {
var thisChapterStart = parseInt(cues[theChapter]["chapterStart"]+1);
theVideo.currentTime=thisChapterStart/frameRate;
}
}
However, be aware that this is not the best practice for binding a function to an event as it makes skipToChapter global and uses inline event handlers (http://robertnyman.com/2008/11/20/why-inline-css-and-javascript-code-is-such-a-bad-thing/).
A better approach would be:
function buildChapterList() {
for(var i=0; i<Object.keys(theChapterCues).length; i++) {
chapterListHTML = "<li><a class='fun-blue' href='javascript:void(0)' data-index='" + i + "'>"+ theChapterCues[i]["title"] +"</a></li>";
var $chapterListHTML = $(chapterListHTML);
$chapterListHTML.appendTo("ul#chapter-list");
$chapterListHTML.find('a').click(skipToChapter);
$("body").bind("DOMNodeInserted", function() {
$(this).find('#chapter-list li').first().addClass("active");
});
}
}
function skipToChapter() {
var theChapter = $(this).data('index');
if (theChapter == 0) {
theVideo.currentTime=0;
} else {
var thisChapterStart = parseInt(cues[theChapter]["chapterStart"]+1);
theVideo.currentTime=thisChapterStart/frameRate;
}
}
Read this answer for more information on event binding on dinamically created elements, with jQuery: Event binding on dynamically created elements?

Related

How can I modify an anonymous javascript function with tampermonkey?

Here is the block of code I want to replace:
$(document).ready(function () {
$(".button-purple").click(function () {
interval = $(this).attr('id');
name = $(this.attr('name');
if(Number($(this).val()) === 0) {
if(name == 'static') {
do this
}
else {
do this
}
}
else {
do this
}
});
});
I can't find any documentation on trying to replace the function since it's unnamed though. Is it possible to replace the entire javascript file + delete the line loading it / insert my own script? Would really appreciate any help I can get.
If you just want to remove the click event handler, then simply say
var $element = $(".button-purple");
$element.off('click');
If you want to Remove all the event handlers, then you'll first have to find out what all event handlers are present and then remove them iteratively.
var element = $element[0]; //Make sure the element is a DOM object and not jQuery Object.
// Use this line if you're using jQuery 1.8+
var attachedEvents = $._data(element,'events');
// Use this line if you're using jQuery < 1.8
var attachedEvents = $(element).data('events'); //Here you can also replace $(element) with $element as declared above.
for(var event in attachedEvents){
$element.off(event);
}
UPDATE:
You can simply add your own event handler (using .on() API) after you're done removing all the required existing handlers.
Just define your function.
function yourFunction(){ /* your code */};
$element.on('click', yourFunction);
Update 2:
Since you just want to remove the click event handler, this is the simplest code that will serve your purpose.
$(".button-purple").off('click').on('click', yourFunction);
I'm not aware of tampermonkey, but you can try this:
function chickHandler() {
interval = $(this).attr('id');
name = $(this.attr('name');
if (Number($(this).val()) === 0) {
if (name == 'static') {
do this
} else {
do this
}
} else {
do this
}
}
}
function onReadyHandler() {
$(".button-purple").click(chickHandler);
}
$(document).ready(onReadyHandler);
When you do something like .click(function(){...}), here function is called as a callback. You have to send a function as a callback. Not necessary to be anonymous.

Call function is not a function

$(function() {
var previous_page = "<?=$_SESSION["previous_page"]?>";
if (previous_page == "bar_settings")
$.club_settings();
$.club_settings = function() {
$(".bar_settings").fadeIn(1000);
$(".bar_photos").hide();
$(".bar_activities").hide();
$(".bar_campaigns").hide();
$(".etkinlik_ekle").hide();
$(".kampanya_ekle").hide();
}
})(jQuery);
I got an error that is $.club_settings is not a function. How can i call $.club_settings in a if condition ?
You're defining the function after you call it. Switch around the code like so:
$(function() {
$.club_settings = function() {
$(".bar_settings").fadeIn(1000);
$(".bar_photos").hide();
$(".bar_activities").hide();
$(".bar_campaigns").hide();
$(".etkinlik_ekle").hide();
$(".kampanya_ekle").hide();
}
var previous_page = "<?=$_SESSION["previous_page"]?>";
if (previous_page == "bar_settings")
$.club_settings();
})(jQuery);
JavaScript only hoists declarations, not initializations.
The reference above displays how variables are hoisted but it works for functions too.
$(function() {
var previous_page = "<?=$_SESSION["previous_page"]?>";
if (previous_page == "bar_settings")
club_settings();
function club_settings() {
$(".bar_settings").fadeIn(1000);
$(".bar_photos").hide();
$(".bar_activities").hide();
$(".bar_campaigns").hide();
$(".etkinlik_ekle").hide();
$(".kampanya_ekle").hide();
}
})(jQuery);
The drawback would be is that it would be found in your $ variable which may lead to codes elsewhere breaking. But that could be another question.
Trying to resolve this by doing $.club_settings() = function club_settings() { ... will not work unless you reorder your codes as suggested by Mike

Cross-browser addevent function javascript

I'm trying to create a function that adds an event to each button in a class. I have all of the buttons in an array and wanted to use Dustin Diaz's addevent() cross browser solution, but am unsure how to implement it. I'm used to using frameworks for this sort of thing, but have to use pure JS for this one.
Any pointers or advice on how to use Dustin's solution would be appreciated.
Ok so after taking #Pointy 's advice, I wrote this that checks for addEventListener and if not uses attachEvent This however is not calling testFunction(). What am I doing wrong here?
function addEvents()
{
var buttonArray=document.getElementsByClassName('mainButton');
for(i=0; i < buttonArray.length; i++)
{
if (i.addEventListener) {
i.addEventListener("click", testFunction);
}
else if (i.attachEvent) {
i.attachEvent("onclick", testFunction);
}
function testFunction() {
alert("Test");
}
}
// Attach an event to each button that when clicked on will display an alert that say 'Button X clicked' where X = the button ID
}
You are trying to add an event to a number. You should replace "i" with "buttonArray[i]" and add an else-case (defensive coding).
function addEvents() {
var buttonArray = document.getElementsByClassName('mainButton');
for(i = 0; i < buttonArray.length; i++) {
if (buttonArray[i].addEventListener) {
buttonArray[i].addEventListener("click", testFunction);
} else if (buttonArray[i].attachEvent) {
buttonArray[i].attachEvent("onclick", testFunction);
} else {
throw new Error("This should be unreachable");
}
}
function testFunction() {
alert("Test");
}
}

Javascript - Uncaught ReferenceError - Code seems perfect?

Here's how I'm calling my JS:
"#item.OwnerID#" is a variable from a loop containing an ID. So the element I want to change the CSS for should look like: "cwa123" or some other number for the id...
Here's my JS:
$(document).ready(function() {
function toggleChatControl(id){
var wnd = document.getElementById(id);
if (wnd.style.marginBottom == '-1px') {
wnd.style.marginBottom = '-236px';
} else {
wnd.style.marginBottom = '-1px';
}
}
});
I ain't got a clue, it gives me the "not defined" error...
Out of scope, remove the document ready wrapper
function toggleChatControl(id){
var wnd = document.getElementById(id);
if (wnd.style.marginBottom == '-1px') {
wnd.style.marginBottom = '-236px';
} else {
wnd.style.marginBottom = '-1px';
}
}
Every function creates a new scope, the global scope is window, and that's the scope used for inline javascript.
Inside $(document).ready(function() { ... }); the scope is changed (to document) so the function is out of scope for the inline handler.
An even better approach would be to use a proper event handler
$('.FCChatControl').on('click', function() {
toggleChatControl('cwa#item.OwnerID#');
});

Parse for links that have target="new" and add an onclick event

I have a web site that gets links generated dynamically. I would like to see if I can add an onclick event handler to track external links. I am looking to see links that has target="new" ( which means external to our site) and add the event handler
html code
<a target="new" href="http://twitter.com/cnn">CNN</a>
The code I tried to test is not working. Let me know what is wrong in my code or should I some home append the onclick event to the external links?
Js code
var links = document.getElementsByTagName("a");
for (var i=0; <links.length; i++) {
if(links[i].target == 'new'){
links[i].onclick = function() {
alert("Added onClick: " + links[i].href);
}
}
}
Another answer here is what you should go with (using this) but it's worth addressing the issue of closures in for loops.
If you want to use a variable that changes for each iteration in a for loop in a closure that's created in that for loop, define and call an anonymous function that returns a function to be bound (binded?) to the onclick event.
var links = document.getElementsByTagName("a");
for (var i=0; i<links.length; i++) {
if(links[i].target == 'new'){
links[i].onclick =
function (obj) {
return function(event) {
alert("Added onClick: " + obj.href);
}
}(links[i]);
}
}
Since the parameter (obj) to the anonymous function is passed by value it won't change in subsequent iterations for the for loop. The returned function will have its own copy of the object.
A lot of stuff in Javascript starts making sense when you think of functions as objects that can be passed around.
You can use this in your onclick function and avoid the closure issue
var links = document.getElementsByTagName("a");
for (var i=0; i<links.length; i++) {
if(links[i].target == 'new'){
links[i].onclick = function(){
alert("Added onClick: " + this.href);
}
}
}
are you adverse to jQuery?
$("a[target='new']").click(function(){alert($(this).attr("href"));});

Categories

Resources