I'm trying to implement smoothState.js into a custom WordPress theme. I'm flummoxed. I feel like my code is correct according to the smoothState documentation, but it's still producing an error. But I'm still a beginner at JS/JQuery and scripting in general so I'm probably missing something.
Using JQMigrate version 1.4.1 and JQuery 1.12.4 in noConflict mode. WordPress is 4.6.1.
jQuery(document).ready(function($){ // WordPress doesn't release $ so we need this line
$(function() {
var $body = $('html, body'),
content = $('#wrapper').smoothState({
prefetch: true,
pageCacheSize: 4,
debug: true,
// onStart is for when a link is clicked
onStart: {
duration: 2500, // animation duration in ms
render: function (url, $container) {
$container.addClass('is-exiting');
$body.animate({
scrollTop: 0
});
smoothState.restartCSSAnimations();
}
},
onEnd : {
duration: 1500, \
render: function (url, $container, $content) {
$container.removeClass('is-exiting');
$body.css('cursor', 'auto');
$body.find('a').css('cursor', 'auto');
$container.html($content);
// Trigger document.ready and window.load
$(document).ready();
$(window).trigger('load');
}
},
onAfter : function(url, $container, $content) {
}
}).data('smoothState');
});
(function($, undefined) {
var isFired = false;
var oldReady = jQuery.fn.ready;
$(function() {
isFired = true;
$(document).ready();
});
jQuery.fn.ready = function(fn) {
if(fn === undefined) {
$(document).trigger('_is_ready');
return;
}
if(isFired) {
window.setTimeout(fn, 1);
}
$(document).bind('_is_ready', fn);
};
})(jQuery);
});
Clicking any hyperlink inside #wrapper throws the following console error...
Uncaught TypeError: Cannot read property 'addClass' of undefined
w # jquery.smoothState.min.js:9
b # jquery.smoothState.min.js:9
dispatch # jquery.js?ver=1.12.4:3
r.handle # jquery.js?ver=1.12.4:3
I've checked for any potential plugin or theme conflicts and found none. I've even tried explicitly declaring the container like var $container = $("#wrapper"); but still get the same results. Any ideas?
After looking over the smoothState onStart documentation it seems the render function only accepts a single argument like so:
$('#main').smoothState({
onStart: {
// How long this animation takes
duration: 0,
// A function that dictates the animations that take place
render: function ($container) {}
}
});
Your code has two:
render: function (url, $container) {
This would likely cause your render function url argument to be set to $container as it is the first argument passed, and the $container argument to be undefined, as the argument is never passed/set. Thus, when you call:
$container.addClass('is-exiting');
You receive the error:
Cannot read property 'addClass' of undefined
Because $container is not an object.
Related
I am trying to compile a basic jQuery plugin which shows a div upon provided options when invoking:
select if a checkbox should be checked after X milliseconds or after X px on scroll,
if one of those two options are selected, set a delay value or scroll distance in px
otherwise do nothing
Example of desired options invoke:
$(document).ready( function() {
$('#testInput').testing({
myMethod : delay,
myValue : 2000
});
});
My current progress here: JSFiddle (it's not much as currently I'm still at a beginning of the learning curve)
After some fiddling i managed to get this working (kinda). Plugin seems to be working fine. Except there is some bug with the scroll function which i have to sort it out additionaly - it loops and changes checkbox states indefinitely instead just once.
Here is a working fiddle
And modified code:
(function($) {
$.fn.testing = function( options ) {
// Settings
var settings = $.extend({
delay : null,
delayTime : null,
scrolling : null,
scrollDist : null
}, options);
return this.each( function() {
var self = this;
// Timeout
setTimeout(function (){
$(self).prop('checked', settings.delay);
}, settings.delayTime);
// Scroll
if ($(window).scrollTop() > settings.scrollDist) {
$(this).prop('checked', settings.scrolling);
};
});
}
}(jQuery));
// Plugin invoke
$(window).on("load resize scroll",function(){
$('#testInput').testing({
delay : false,
delayTime : null,
scrolling : true,
scrollDist : 20,
});
});
I am trying to "do some stuff" at the end of this crazy animation loop... The alert I have commented out at the end of the animation works great... However, what I really need it to do is remove/hide the closest div with a class of .item (something like this)
var jremove = $(this).closest('.item').hide();
$container.masonry('remove', jremove );
//Then trigger a reload function for masonry
reloadMasonry(); // not working yet
When I try do do anything but alert a simple message I get an error like this:
Uncaught TypeError: Object #<HTMLImageElement> has no method 'closest'
You can see it at the bottom of the animation below:
jQuery(function ($) {
$("body").on("click", ".clip_it", function () {
if ($(this).parent().find(".clip_it").length<1){
$(this).after('<a class="clip_it" href="javascript:void(0)" onclick="">CLIP IT!</a><img src="http://img.photobucket.com/albums/v29/wormholes201/animated-scissors.gif" class="ToBeAnimated">');
}
animationLoop($(this).closest(".item-inner").eq(0),$(this).parent().find(".ToBeAnimated").eq(0));
});
});
function animationLoop(ctx,ctx2) {
ctx2.fadeIn();
ctx2.css({
top: (0 - parseInt(ctx2.height()) / 2),
left: (0 - parseInt(ctx2.width()) / 2),
position:"absolute",
"z-index":800
}).rotate(270);
ctx2.animate({
top: ctx.height() - ctx2.height() / 2
}, 1000, function () {
ctx2.animate({
rotate: "180deg"
}, 1000, function () {
ctx2.animate({
left: ctx.width() - ctx2.width() / 2
}, 1000, function () {
ctx2.animate({
rotate: "90deg"
}, function () {
ctx2.animate({
top: 0-ctx2.height() / 2
}, 1000, function () {
ctx2.animate({
rotate: "0deg"
}, function () {
ctx2.animate({
left: (0 - parseInt(ctx2.width()) / 2)
}, 1000, function () {
setTimeout(animationLoop(ctx,ctx2), 1000);
//I want to remove the coupon (.item) & reload masonry here
// TEST ALERT WORKS = alert("animation complete");
var jremove = $(this).closest('.item').hide();
$container.masonry('remove', jremove );
reloadMasonry();
return false;
});
});
});
});
});
});
});
}
I am open to other suggestions if you think there is a better way? THANKS FOR HELPING!
Uncaught TypeError: Object #<HTMLImageElement> has no method 'closest'
The problem is that $ outside of your ready handler (the function you're passing into jQuery(function($) { ... });) is not pointing to jQuery, it's pointing to something else (my guess would be Prototype or MooTools). So $(this) returns an HTMLImageElement (probably enhanced by Prototype or MooTools) rather than a jQuery object, and so it has no closest function.
Either move the animationLoop function into your ready handler (so it sees the $ that jQuery passes into the ready handler instead of the global), or use jQuery instead of $ in that function.
I have a jQuery plugin that uses namespacing (methods) and also has options, with defaults, that can be overridden on initialization.
I'm wondering what the best way to define and use options is with this plugin in the namespaces.
I was originally using a $.fn.dropIt.settings within the wrapper function to define the settings, but then switched to defining them inside of the init method. This is very limiting in terms of scope however..
Here is the relevant code in my plugin
(function($, window, document, undefined){
var methods = {
init: function(options)
{
var settings = $.extend({
trigger: "hover",
animation: 'slide', /* none, slide, fade, grow */
easing: 'swing', /* swing, linear, bounce */
speedIn: 400,
speedOut: 400,
delayIn: 0,
delayOut: 0,
initCallback: function(){},
showCallback: function(){},
hideCallback: function(){}
}, options);
$(this).each(function(index, ele){
$ele = $(ele);
$ele.addClass('dropit');
//Attach event handlers to each list-item
$('li', $ele).dropIt('attach', settings);
//If list is displayed veritcally, add extra left padding to all sub-menus
if($(ele).hasClass('vertical'))
{
$('li', $ele).find('ul').addClass('nested sub-menu');
} else {
$('li ul', $ele).addClass('nested').find('ul').addClass('sub-menu');
}
});
//Call custom callback
settings.initCallback.call();
//Return jQuery collection of lists
return $(this);
},
attach: ...
_trigger: ...
_hide: ...
}
};
$.fn.dropIt = function(method){
//Variables and Options
var $this = $(this);
// Method calling logic
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.dropIt' );
}
};
})(jQuery, window, document);
After reading the jQuery Plugins/Authoring page, I structured by plugin basically like this:
(function ($) {
var defaults = {
// set default options
}
// internal functions
var methods = {
// plugin methods
}
$.fn.pluginName = function (method) {
}
})(jQuery);
And, like you have, $.extend the defaults within the init method, but I like to keep the defaults declared separately, personally, for clarity. It's been working well for me.
I always set the settings object as a global var to the plugin scope, like this:
(function($, window, document, undefined){
var settings; //NOTE THIS LINE
var methods = {
init: function(options)
{
settings = $.extend({ //AND THIS LINE
trigger: "hover",
animation: 'slide', /* none, slide, fade, grow */
easing: 'swing', /* swing, linear, bounce */
speedIn: 400,
speedOut: 400,
delayIn: 0,
delayOut: 0,
initCallback: function(){},
showCallback: function(){},
hideCallback: function(){}
}, options);
$(this).each(function(index, ele){
$ele = $(ele);
$ele.addClass('dropit');
//Attach event handlers to each list-item
$('li', $ele).dropIt('attach', settings);
//If list is displayed veritcally, add extra left padding to all sub-menus
if($(ele).hasClass('vertical'))
{
$('li', $ele).find('ul').addClass('nested sub-menu');
} else {
$('li ul', $ele).addClass('nested').find('ul').addClass('sub-menu');
}
});
//Call custom callback
settings.initCallback.call();
//Return jQuery collection of lists
return $(this);
},
attach: ...
_trigger: ...
_hide: ...
}
};
$.fn.dropIt = function(method){
//Variables and Options
var $this = $(this);
// Method calling logic
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.dropIt' );
}
};
})(jQuery, window, document);
EDIT:
You can also do $(this).data('youPlugInName', settings), then if you want to change it latter you can retrieve this from data('yourPlugInName'), and update whatever property you want.
I'm trying to augment the (very nice) jQuery Spotlight plugin so that I can programmatically invoke the "hide" behavior.
I've moved the related code into a hide() function, and it works fine when invoked from within spotlight itself. But when I try to invoke it from outside of spotlight nothing happens. I've checked that spotlight.hide is in fact defined to type function, but invoking it seemingly does nothing.
(function($) {
$.fn.spotlight = function(options) {
var hide = function() {
alert('hiding...'); /* never gets invoked when called from outside spotlight */
if(settings.animate){
spotlight.animate({opacity: 0}, settings.speed, settings.easing, function(){
if(currentPos == 'static') element.css('position', 'static');
element.css('z-index', '1');
$(this).remove();
// Trigger the onHide callback
settings.onHide.call(this);
});
} else {
spotlight.css('opacity', '0');
if(currentPos == 'static') element.css('position', 'static');
element.css('z-index', '1');
$(this).remove();
// Trigger the onHide callback
settings.onHide.call(this);
}
};
// Default settings
settings = $.extend({}, {
opacity: .5,
speed: 400,
color: '#333',
animate: true,
easing: '',
exitEvent: 'click',
onShow: function(){},
onHide: function(){}
}, options);
// Do a compatibility check
if(!jQuery.support.opacity) return false;
if($('#spotlight').size() == 0){
// Add the overlay div
$('body').append('<div id="spotlight"></div>');
// Get our elements
var element = $(this);
var spotlight = $('#spotlight');
// Set the CSS styles
spotlight.css({
'position':'fixed',
'background':settings.color,
'opacity':'0',
'top':'0px',
'left':'0px',
'height':'100%',
'width':'100%',
'z-index':'9998'
});
// Set element CSS
var currentPos = element.css('position');
if(currentPos == 'static'){
element.css({'position':'relative', 'z-index':'9999'});
} else {
element.css('z-index', '9999');
}
// Fade in the spotlight
if(settings.animate){
spotlight.animate({opacity: settings.opacity}, settings.speed, settings.easing, function(){
// Trigger the onShow callback
settings.onShow.call(this);
});
} else {
spotlight.css('opacity', settings.opacity);
// Trigger the onShow callback
settings.onShow.call(this);
}
// Set up click to close
spotlight.live(settings.exitEvent, hide);
}
// Returns the jQuery object to allow for chainability.
return this;
};
})(jQuery);
I install it with:
var spotlight = $('#media-fragment').spotlight({
opacity: .5,
speed: 400,
color: '#333',
animate: false,
easing: '',
exitEvent: 'click',
onShow: function(){},
onHide: function(){}
});
And then to hide it I do:
spotlight.hide();
I'm pretty sure that there is a scope or this issue involved.
Update: full solution at https://gist.github.com/2910643.
Try changing:
var hide = function() {
to:
this.hide = function() {
var defines the scope of the function or variable within the parent scope, i.e. it's essentially protected. this on the otherhand will explicitly set it on the parent object, prototype, and make it publicly accessible.
I think my problem is very small, but i still need for help in my jquery code, I think it's all about js syntax:
$(function() {
// Options for SuperBGImage
$.fn.superbgimage.options = {
randomtransition: 2, // 0-none, 1-use random transition (0-7)
z_index: -1, // z-index for the container
slideshow: 1, // 0-none, 1-autostart slideshow
slide_interval: 2000, // interval for the slideshow
randomimage: 1, // 0-none, 1-random image
speed: 'slow' // animation speed
};
// initialize SuperBGImage
$('#thumbs').superbgimage().hide();
});
$(function() {
var $j = jQuery.noConflict();
$j(document).ready(function(){
$j('.menu-nav li').hover(
function() {
$j(this).addClass("active");
$j(this).find('.ulwrapper').stop(false, true).fadeIn();
$j(this).find('.ulwrapper .ulwrapper').stop(false, true).fadeOut('fast');
},
function() {
$j(this).removeClass("active");
$j(this).find('div').stop(false, true).fadeOut('fast');
}
);
$j('.ulwrapper').hover(
function() {
$j('.parent').addClass("active_tab");
},
function() {
$j('.parent').removeClass("active_tab");
}
);
});
});
the first code is for the Big image where the other is to work with my multi-level menu. I tried to workaround this by changing
$(function() {
to
$j(document).ready(function(){
but the browser only runs one code! and gives me:
Uncaught TypeError: Property '$' of object [object DOMWindow] is not a function
(anonymous function)
I am waiting your help, thanks too much.
Once you call jQuery.noConflict, the "$" binding won't work anymore. You don't say why you're calling it, but you can use just "jQuery" instead of "$". Your "$j" variable is local to that function where you call "noConflict".
Also, it doesn't make sense to do this:
$(function() {
$(function() {
// ...
});
});
which is what you're doing in that second "ready" handler. That is, you're setting up a "ready" handler from inside another one, which makes no sense really.
For using $j in your script,you should insert following code:
<script type="text/javascript">
$j=jQuery.noConflict();
</script>
Best Luck..