I'm trying to access the selector of the jQuery object inside of the closure below so that I don't have to specify it and/or cache it. If I replace $(".the_lead") with $this, it won't perform it's action.
Invoking the plugin
$(".the_lead").scroll_lead({speedup: 400});
Block
var $this = $(this);
$(window).scroll(function() {
var window_height = $(window).height();
var document_height = $(document).height();
var hide_lead;
var scrollTop = $(window).scrollTop();
console.log($this);
if(!hide_lead){
if(scrollTop>(document_height/2)){
$(".the_lead").slideDown(options.speedup);
}else{
$(".the_lead").slideUp(500,function(){
$(".the_lead").hide();
});}
}
$('#hide_lead').click(function(e){
//$(".the_lead").parent().parents('div').hide();
hide_lead = true;
e.preventDefault();
});
});
Console Output for $(this):
[selector: ".the_lead", context: document, constructor: function, init: function, selector: ""…]
context: #document
length: 0
selector: ".the_lead"
__proto__: Object[0]
Notice the length: 0 output by the console. The window scroll event fires more or less every pixel the user moves. Without seeing more of your plugin's code I couldn't be sure, but I'd bet $this is correct only the first time the scroll event fires.
Additionally, caching it seems like you'd want to cache only one of the .the_lead elements, no? That's how your code reads. with $this = $(this); but then within the function you seem to want all elements with the class .the_lead. Try directly caching it like this instead. $this = $('.the_lead');
Related
I am working on MVC application and I have jQuery plugin, calculating width and height of page and I am calling this from document.ready function. I am getting following error
ReferenceError: getPreventDefault is not defined MyCustomScript:1:115
Use of getPreventDefault() is deprecated. Use defaultPrevented instead. jquery-1.10.2.js:5389:0
no element found
my plugin
(function($) {
$.fn.adjustOuterStripLayout = function () {
alert("strip");
$(window).bind('load resize', function () {
var viewport_height = $(window).height();
var viewport_width = $(window).width();
var webPage_containerWidth = $('.container').width();
alert("viewport_width " + viewport_width + "container " + webPage_containerWidth);
});
};
})(jQuery);
main function
$(document).ready(function () {
alert("hello");
$(this).adjustOuterStripLayout();
});
sometime it alert and sometime it not. I have also clear browser cashed and testing this on firefox and jquery version 1.10.2
Can you try the same in 'window.onload' event instead of in 'documen.ready' ?
As, you are trying to refer the window object to get height & width. Unless its not loaded you wont get that value.
Try using code like:
$(window).load(function () {
$(this).adjustOuterStripLayout();
});
$(function(){
$(".youtubeThumb").each(function(){
$(this).bind('click touchstart', function(event){
event.preventDefault();
var ytLink = $(this).children('img').attr('src');
//the line below is what keeps breaking
var ytLink = ytLink.split('/vi/').pop().split('/').shift();
var ytPlayList = 'PLDMZzXD-QCET2VK_l9aGhOXNMZjWjzI0g';
$('.youtubeEmbed iframe').attr('src', '//www.youtube.com/embed/'+ytLink+'?list='+ytPlayList+'&wmode=transparent&autoplay=1');
$('html, body').animate({
scrollTop: $('.youtubeEmbed').offset().top
}, 600);
})
})
});
//Error Below from console
Uncaught TypeError: Cannot read property 'split' of undefined ?page_id=28:169(anonymous function) ? page_id=28:169n.event.dispatch jquery-2.1.1.min.js?ver=2.1.1:3r.handle
I have never really worked with wordpress before so I could be way off on what I am trying to do.
The link to the page is http://twistedmess.com/?page_id=28
and as you see here on a separate page the code works fine http://schottmandesign.com/test3
I have tried putting the script in a separate js file and then putting it into the header.php, putting it into the actual page itself, and right now it is being contained in a shortcode plugin that I am then calling on the actual page. Any help is much appreciated.
what you want instead of children, is find. you want to look recursively through all the elements in the jquery object. children is only one level below parent deep. see this fiddle for a toy example http://jsfiddle.net/gq36et5x/1.
instead your code should look like this:
$(function(){
$(".youtubeThumb").each(function(){
$(this).bind('click touchstart', function(event){
event.preventDefault();
var ytLink = $(this).find('img').attr('src');
//the line below is what keeps breaking
var ytLink = ytLink.split('/vi/').pop().split('/').shift();
var ytPlayList = 'PLDMZzXD-QCET2VK_l9aGhOXNMZjWjzI0g';
$('.youtubeEmbed iframe').attr('src', '//www.youtube.com/embed/'+ytLink+'?list='+ytPlayList+'&wmode=transparent&autoplay=1');
$('html, body').animate({
scrollTop: $('.youtubeEmbed').offset().top
}, 600);
})
})
});
also, its not necessary to run the each loop to bind to click event. you could just slim things down this way:
$(function(){
$(".youtubeThumb").click(function(e){
e.preventDefault();
var ytLink, ytPlaylist;
ytPlaylist = 'PLDMZzXD-QCET2VK_l9aGhOXNMZjWjzI0g';
ytLink = $(this).find('img').attr('src').split('/vi/').pop().split('/').shift();
$('.youtubeEmbed iframe').attr('src', '//www.youtube.com/embed/'+ytLink+'?list='+ytPlayList+'&wmode=transparent&autoplay=1');
$('html, body').animate({ scrollTop: $('.youtubeEmbed').offset().top}, 600);})
})
});
The img tag is not a direct child of the .youtubeThumb element, therefore change children to find. Moreover, find will return an array of elements (or an empty array if there are no matches), so, you will need to check the content of what find returns before you try to access the src attribute;
var ytThumb = $(this).find('img');
if (ytThumb.length > 0) {
var ytLink = ytThumb[0].attr('src');
// ... the rest of the code
}
EDIT: Got answered by gabereal in the meantime.
I'm using jquery 1.9.1 and I'm trying to develop a plugin. The problem is that the plugin isn't working. Here's the code:
;(function($) {
$.fn.single = function() {
return this.each(function(){
// Get the instance
var element = $(this);
// Resize the "data-target" divs
element.load(function(){
changeCSS(element);
});
// Bind the method to the resize window event
$(window).bind("resize", function(){
changeCSS(element);
});
});
};
// function to resize all the "data-target" divs
function changeCSS(element) {
// Grab the screen resolution
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Count how many targets the div has
var targetsSize = $("[data-target]").size();
// Resize the parent div
$(element).css({
"width" : windowWidth,
"height": windowHeight * targetsSize
});
// Resize all the targets div
$(element + "> div[data-target]").each(function(){
$(this).css({
"width" : windowWidth,
"height": windowHeight
});
});
}
})(jQuery);
And I'm calling it on the document like that:
<script src="js/jquery-1.9.1.min.js"></script>
<script src="js/single-0.1.0.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#single").single();
});
</script>
There's no problem in the console. What I'm doing wrong?
I'm assuming that it is because you are misusing the method .load. If you look at the jQuery docs, it is intended for:
.load() : Load data from the server and place the returned HTML
into the matched element.
http://api.jquery.com/load/
Remove the lines element.load(function ..., simply call your changeCSS function, you're already loading this extension on Document.Ready
return this.each(function () {
// ...
changeCSS(element); // <-- just run the function right away
// ... etc
});
Generally it is bad practice to call a function before it is declared. To get your plugin right, you will be better of starting structuring it correctly from the beginning. Besides that, as #mcpDESIGNS point out, you are using the .load method wrongly. Also, it would be useful if you explain what exactly it is you are trying to accomplice here.
To get a good start making jQuery plugins, try to look at the documentation at jQuery here or you can look at this tutorial.
This is the preferred structure:
(function($) {
// Declare your methods at the beginning of your plugin
var yourMethods = {
'methodOne' : function() {
},
'methodTwo' : function() {
}
};
$.fn.pluginName = function() {
return this.each(function() {
});
}
}(jQuery));
Instead of:
element.load(function(){
changeCSS(element);
});
I did:
changeCSS(element);
And... Instead of:
$(element + "> div[data-target]")
I did:
$(element).children('div[data-target]')
And now the plugin is being executed with no bugs or errors.
Thanks Guys!
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.
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.