I have a problem with my variable scope in a simple slider script that I´ve written (I don't want to use a readymade solution because of low-bandwidth). The slider script is called on statically loaded pages (http) as well as on content loaded through AJAX. On the statically loaded page (so no AJAX) the script seems to work perfect. However when called through AJAX the methods called can't find the elements of the DOM, which halts the necessay animation that is needed for the slider.
All the events are handled through even delegation (using jQuery's on() function), this however provided no solution. I'm quite sure it has something to do with the structure and variable scope of the script, but am unable to determine how to change the structure. So I'm looking for a solution that works in both situations (called normal or through AJAX).
I tried to declare the needed variables in every function, this however resulted in some akward bugs, like the multiplication of the intervals I set for the animation, because of the function scope. Hope somebody can help me in the right direction.
// Slider function
(function (window, undefined) {
var console = window.console || undefined, // Prevent a JSLint complaint
doc = window.document,
Slider = window.Slider = window.Slider || {},
$doc = $(doc),
sliderContainer = doc.getElementById('slider_container'),
$sliderContainer = $(sliderContainer),
$sliderContainerWidth = $sliderContainer.width(),
slider = doc.getElementById('slider'),
$slider = $(slider),
$sliderChildren = $slider.children(),
$slideCount = $sliderChildren.size(),
$sliderWidth = $sliderContainerWidth * $slideCount;
$sliderControl = $(doc.getElementById('slider_control')),
$prevButton = $(doc.getElementById('prev')),
$nextButton = $(doc.getElementById('next')),
speed = 2000,
interval,
intervalSpeed = 5000,
throttle = true,
throttleSpeed = 2000;
if (sliderContainer == null) return; // If slider is not found on page return
// Set widths according to the container and amount of children
Slider.setSliderWidth = function () {
$slider.width($sliderWidth);
$sliderChildren.width($sliderContainerWidth);
};
// Does the animation
Slider.move = function (dir) {
// Makes use of variables such as $sliderContainer, $sliderContainer width, etc.
};
// On ajax call
$doc.on('ajaxComplete', document, function () {
Slider.setSliderWidth();
});
// On doc ready
$(document).ready(function () {
Slider.setSliderWidth();
interval = window.setInterval('Slider.move("right")', intervalSpeed);
});
// Handler for previous button
$doc.on('click', '#prev', function (e) {
e.preventDefault();
Slider.move('left');
});
// Handler for next button
$doc.on('click', '#next', function (e) {
e.preventDefault();
Slider.move('right');
});
// Handler for clearing the interval on hover and showing next and pervious button
$doc.on('hover', '#slider_container', function (e) {
if (e.type === 'mouseenter') {
window.clearInterval(interval);
$sliderControl.children().fadeIn(400);
}
});
// Handler for resuming the interval and fading out the controls
$doc.on('hover', '#slider_control', function (e) {
if (e.type !== 'mouseenter') {
interval = window.setInterval('Slider.move("right")', intervalSpeed);
$sliderControl.children().fadeOut(400);
}
});
})(window);
The HTML example structure:
<div id="slider_control">
<a id="next" href="#next"></a>
<a id="prev" href="#prev"></a>
</div>
<div id="slider_container">
<ul id="slider">
<li style="background-color:#f00;">1</li>
<li style="background-color:#282">2</li>
<li style="background-color:#ff0">3</li>
</ul>
</div>
I notice you have
Slider.setSliderWidth = function() {
$slider.width($sliderWidth);
$sliderChildren.width($sliderContainerWidth);
};
which is called on ajax complete.
Does you ajax update the DOM giving a new DOM element that you could get to by doc.getElementById('slider')? Then your var slider and jquery var $slider are likely pointing to things that no longer exist (even if there is a dom element with slider as the id). To rectify, whenever the ajax is invoked that replaces that element, reinitialize slider and $slider to point to the new jquery wrapped element using the same initialization you have.
slider = doc.getElementById('slider');
$slider = $(slider);
Edit:
I'm not sure where you're going with the variable scope issue, but take a look at this example.
<pre>
<script>
(function(){
var a = "something";
function x (){
a += "else";
}
function y() {
a = "donut";
}
function print (){
document.write(a +"\n");
}
print ();
x();
print ();
y();
print ();
x();
print ();
})();
document.write(typeof(a) + "\n");
</script>
</pre>
It outputs into the pre tag
something
somethingelse
donut
donutelse
undefined
This isn't all that different from what you're already doing. As long as a is not a parameter of a method and is not declared with var in a nested scope, all references to a in code defined within your function(window,undefined){ ...} method will refer to that a, given that a is defined locally by var to that method. Make sense?
To begin, surely you can replace all the getElementById using a jQuery approach. i.e. replace $(doc.getElementById('next')) with $('#next')
I think that when you use on it doesn't search the element for the selector as you are assuming. So you would have to use:
$doc.on('click', '#slider_control #prev',function(e){
e.preventDefault();
Slider.move('left');
});
Wait, what gets loaded through Ajax? The slider-html code? In that case, the Slider has already been 'created' and a lot of your variables will point to nowhere (because these DOM elements did not existed when the variables were initialized). And they will never do so either.
Related
I'm trying to create a simple click catcher where if you click .image-class the javascript will take the href from another element with a class name of .btn and send you to it's destination. Though I keep getting errors on lines 7 & 10 saying that undefined is not a function. How do I make this work?
<script>
var ClickCatcher=
{
init:function(){
var link = jQuery('.btn')[1].href;
var imgCatch = jQuery('.image-class');
imgCatch.addEventListener("click", ClickCatcher.clickListener, false);
},
clickListener:function(){
window.location = link;
}
};
ClickCatcher.init();
</script>
You can do this with jquery with a simple click event
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
But if you still want to write in the way you have you can do:
var ClickCatcher = {
init: function () {
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
}
};
ClickCatcher.init();
Just make sure to fire the init method after dom load.
update: One issue with it is that you have coded your target etc in the code rather then pass it, so its going to be hard to reuse, you'd be better off doing:
var ClickCatcher = {
init: function ($button, loc) {
$button.on('click', function (){
window.location = loc;
});
}
};
ClickCatcher.init(jQuery('.image-class'), jQuery('.btn').eq(1).attr('href'));
That way the internal working is seperate from the dom (as you are passing the dom dependencies to the function.
#atmd showed a very good way of doing this. If you just want to know what your mistake was though. It is wa an error in your jQuery stament to get the btn href
jQuery('.btn')[1].href
you need to call the attr function and then get the href attr. and use .eq(1) to reduce the set to the first btn
jQuery('.btn').eq(1).attr('href);
What I want to do is I have a code like below :
$(document).ready(
function(){
var currentPage = window.location.pathname;
$('#main-menu-list').find('a[href^="' + currentPage + '"]').closest('li').addClass('active');
}
)
And now I want to add this code to add and get work with other code. I need to add this code after this one:
function () {
/* If there are forms, make all the submit buttons in the page appear
disabled and prevent them to be submitted again to avoid accidental
double clicking. See Issue 980. */
jQuery(function() {
/* Delegate the function to document so it's likely to be the last event
in the queue because of event bubbling. */
jQuery(document).delegate("form", "submit", function (e) {
var form = jQuery(this);
if (form.hasClass("form_disabled")) {
e.preventDefault();
return false;
}
else {
form
.addClass("form_disabled")
.find(":submit")
.addClass("disabled");
}
// Reactivate the forms and their buttons after 3 secs as a fallback.
setTimeout(function () {
form
.removeClass("form_disabled")
.find(":submit")
.removeClass("disabled");
}, 3000);
});
});
}
How can I get this done. Please help me out to solve this problem.
You can create document.ready() anywhere in script. It is not necessary all of your code should be in ready function.
You can create instance variable for function and call it where you need:
$(document).ready(
var myFunc = function(){
var currentPage = window.location.pathname;
//other code
}
...
//and invoke it where you need
myFunc();
)
First, name the long function in your code section, for example, launchFormControls(). And then define the function outside of the document ready event. A good practice would be to do so and keep the ready event body clean.
For example:
function launchFormControls() {
//function code
}
Or, in other syntax:
var launchFormControls = function() {
//function code
}
Second, call your function from within the document ready event. Your function will be defined and able to call once the document is loaded. This code can be placed at the top or bottom of your javascript section or file.
For example:
$(document).ready(function(){
var currentPage = window.location.pathname;
$('#main-menu-list').find('a[href^="' + currentPage+'"]').closest('li').addClass('active');
launchFormControls();
});
I'm trying to get the jquery loadmask addon to work that will mask elements (for loading content). I'm using knockout.js, and when if I mask an element outside of my viewmodel it works, but I want to mask it upon submitting a POST request, and then unmask when I receive it. I'm getting an "object has no method mask" error from this. I'm not quite sure how to go about setting up an object to access it.
This works, but it's not what I want. I noted in the code where I would like to call mask from
<div id = "register_container">
<div data-bind="visible: register()">
<div id = "register_form"> <!--this is the div I want to mask -->>
<button data-bind="click: submitRegistration">Submit</button>
</div>
</div>
</div>
function MyViewModel(){
self.submitRegistration = function(){
//I want to mask here. When I try it says Object[object object] has no method mask
$.post....{
if(data.result == success){
// andunmask here
}
}
}
}
$("#register_form").mask("Waiting..."); //the masking works when I place it here, but it's always enabled and I want it inside the viewmodel where I noted so it only works when the POST request is in process
That's great and all, but I want to mask something from inside the viewmodel where I noted. How can I accomplish this?
I see several things that could be the problem.
Frist, you're doing assignment as opposed to comparison in the if statement. Use this instead:
if(data.result == success){
or even
if(data.result === success){
Second is the fact that I don't quite understand your code self.submitRegistration(){, which typically looks more like this:
var MyViewModel = function () {
var self = this;
self.submitRegistration = function() {
};
};
Then, if I mock the $.post call, it would work like this:
var MyViewModel = function () {
var self = this;
self.register = ko.observable(true);
self.submitRegistration = function() {
$("#register_form").mask("Waiting...");
// Mock $.post
window.setTimeout(function () {
if (1 == 1) {
// andunmask here
$("#register_form").unmask();
}
}, 3000);
}
};
ko.applyBindings(new MyViewModel());
See this fiddle for a demo.
You could even have Knockout help you find the element to look for:
See this updated fiddle for a demo of that.
// Use the "event" parameter to find the element...
self.submitRegistration = function(data, event) {
$(event.target).closest('#register_form').mask("Waiting...");
Hope it helps.
Hi all thanks for taking a look.
I am trying to call a javascript function when I click on the update button.
Here is the javascript
var text2Array = function() {
// takes the value from the text area and loads it to the array variable.
alert("test");
}
and the html
<button id="update" onclick="text2Array()">Update</button>
if you would like to see all the code check out this jsfiddle
http://jsfiddle.net/runningman24/wAPNU/24/
I have tried to make the function global, no luck, I can get the alert to work from the html, but for some reason it won't call the function???
You have an error in the declaration of the pswdBld function in your JavaScript.
...
var pswdBld() = function() {
---^^---
...
This is causing a syntax error and avoiding the load of your JavaScript file.
See the corrected version.
Also, you may consider binding the event and not inlining it.
<button id="update">Update</button>
var on = function(e, types, fn) {
if (e.addEventListener) {
e.addEventListener(types, fn, false);
} else {
e.attachEvent('on' + types, fn);
}
};
on(document.getElementById("update"), "click", text2Array);
See it live.
In your fiddle, in the drop-down in the top left, change "onLoad" to "no wrap (head)"
Then change
var text2Array = function()
var pswdBld() = function()
to
function text2Array()
function pswdBld()
and it will alert as expected.
You have a syntax error in the line below..
var pswdBld() = function
^--- Remove this
supposed to be
var pswdBld = function
Also make sure you are calling this script just at the end of the body tag..
Because you are using Function Expressions and not Function Declaration
var pwsdBld = function() // Function Expression
function pwsdBld() // Function Declaration
Check Fiddle
I have a script that runs a slideshow for my page. I'm trying to use .delegate() to insert a new set of images shown within the slideshow including its thumbnails. I'm using a .load() function to load an external <div> to replace some HTML within the active page. I also have buttons with IDs, (#kick1, #kwick2, etc.) that determine what set of slide show is loaded.
jQuery("#kwick2").click(function () {
jQuery("body").delegate('#slideshow', 'click', function() {
jQuery('#slideshow').load('/design.html #design');
)};
)};
Pretty sure the syntax is all wrong. Can someone help me?
The #slideshow div is something I created to contain some other divs directly
effected by the slideshow script. Within div ID #slideshow are
<div class="main_image">, <div class="desc"> and <div class="image_thumb">.
These are being replaced directly when you click a KWICK button, they are all pretty much self explanatory, image thumb has and unordered list with image links.
You should not re-deligate every time you click the button. You are doing the wrong.
Instead what you should have is something like:
var foobar = (function () {
var func , mod1 , mod2;
mod1 = function () {
/* do something in state 1 */
};
mod2 = function () {
/* do something in state 2 */
};
return {
state: function (e) {
switch (this.id){
case 'kwick1':
func = mod1;
break;
case 'kwick2':
func = mod2;
break;
}
},
callback: function (e) {
func.call();
}
}
})();
jQuery("#kwick1").click( foobar.state ); // and you really should delegate this
jQuery("#kwick2").click( foobar.state );
jQuery("body").delegate('#slideshow','click', foobar.callback);
Or something similar to this ..
And no , i did not test this code. It is written to explain the concept, not to spoon-feed people.
not sure if i have understood the question well, anyway you have to specify what event you are delegating
jQuery("#kwick2").click(function () {
jQuery("body").delegate('#slideshow',"click", function(){
jQuery('#slideshow').load('/design.html #design');
)};
)};
delegate