Need help Converting a jQuery event to a Drupal Behavior - javascript

I have a Drupal 7 web site that is using jQuery animations to fadeIn div tags. I need an event to capture a fadeIn when it is completed. I have found a sample jQuery example that does what I need it to, but I have not been able to successfully convert it to a Drupal 7 behavior and I'm not quite sure what I might be missing.
Fiddle Example
Below is my Drupal JS file, fadeInEvent.js.
Drupal.behaviors.fadeInEvent= {
attach: function (context, settings) {
var _old = jQuery.fn.fadeIn;
jQuery.fn.fadeIn = function() {
var self = this;
_old.apply(this.arguments).promise().done(function(){
self.trigger('fadeIn');
});
};
jQuery('.tab-pane').bind('fadeIn', function() {
alert('fadeIn Done.');
});
}
};
In the above JS code, I never get my alert that the fadeIn has finished on the item I have selected.

Firs of all, while using jQuery in noconflict mode, you may use a closure to access it by $
(function($) {
// jQuery code using $ object.
}(jQuery));
Regarding the .fadeIn(), consider my snippet:
/**
* $.fn.fadeInNew plugin for triggering 'fadeInDone', when .fadeIn is done.
*
* #param speed
* #param easing
* #param callback
* #returns {$.fn}
*/
$.fn.fadeInNew = function(speed, easing, callback) {
var self = this;
self.animate({
opacity: "show"
}, speed, easing, function() {
self.trigger('fadeInDone');
if ($.isFunction(callback)) {
callback.apply(self);
}
});
return self;
}
$('.tab-pane').on('fadeInDone', function() {
alert('Alarm!');
});
$('.button').on('click', function(e) {
$('.tab-pane').fadeInNew();
});
.tab-pane {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tab-pane">Have a good day!</div>
<button class="button">Show something!</button>
Since I don't really like the idea of overriding the native methods of any libraries, I have made a plugin .fadeInNew(), which will trigger custom fadeInDone event on the element. The code behind animation is almoast the same as in native implementation see the source here.
Also, you don't need to use Drupal.behaviors to define something like that. You should use attach only for things that are being loaded with Drupal's ajax framework, see includes/ajax.php

Related

Run function after page load but NOT refresh

I have a jquery function I want to run ONLY when a page is loaded the first time, not after a refresh.
Here's the code:
$(window).on("load",function() {
$("#logo-black").typed({
strings: ["Nothing^450&Co^250.^500", "^800__^400&Co^600."],
typeSpeed: 70,
backSpeed: 100,
callback: function() {
$(".typed-cursor").css("display", "none"),
setTimeout(function(){
$('.main-load').toggleClass('main-load-active'),
$('.nav-text').toggleClass('nav-text-active');
},400),
$('.nav-reveal').toggleClass('nav-reveal-active');
}
});
});
A few things to note:
-I'm using barba.js, so content is added/removed via AJAX.
Code that initializes barba.js for my project:
initFullpagePlugin();
function initFullpagePlugin (parentElement) {
var element;
element = parentElement ? $('#fullpage', parentElement) :
$('#fullpage');
if (element.length) {
// Destroys old fullPage.js object in memory,
// removes elements from DOM
if ($.fn.fullpage.destroy) {
$.fn.fullpage.destroy('all');
}
element.fullpage({
//Scrolling
autoScrolling:false,
scrollingSpeed: 2500,
easing: 'swing',
fitToSection: false
});
}
}
document.addEventListener("DOMContentLoaded", function() {
if (!('webkitClipPath' in document.body.style)) {
alert('Sorry, this demo is available just with webkitClipPath. Try with
Chrome/Safari.');
}
Barba.Pjax.init();
Barba.Prefetch.init();
var FadeTransition = Barba.BaseTransition.extend({
start: function() {
/**
* This function is automatically called as soon the Transition starts
* this.newContainerLoading is a Promise for the loading of the new
container
* (Barba.js also comes with an handy Promise polyfill!)
*/
// As soon the loading is finished and the old page is faded out, let's
fade the new page
Promise
.all([this.newContainerLoading, this.fadeOut()])
.then(this.fadeIn.bind(this));
},
fadeOut: function() {
/**
* this.oldContainer is the HTMLElement of the old Container
*/
return $(this.oldContainer).animate({ opacity: 0 }).promise();
},
fadeIn: function() {
/**
* this.newContainer is the HTMLElement of the new Container
* At this stage newContainer is on the DOM (inside our #barba-
container and with visibility: hidden)
* Please note, newContainer is available just after
newContainerLoading is resolved!
*/
document.body.scrollTop = 0;
var _this = this;
var $el = $(this.newContainer);
$(this.oldContainer).hide();
$el.css({
visibility : 'visible',
opacity : 0
});
initFullpagePlugin($el);
$el.animate({ opacity: 1 }, 400, function() {
/**
* Do not forget to call .done() as soon your transition is finished!
* .done() will automatically remove from the DOM the old Container
*/
_this.done();
});
}
});
/**
* Next step, you have to tell Barba to use the new Transition
*/
Barba.Pjax.getTransition = function() {
/**
* Here you can use your own logic!
* For example you can use different Transition based on the current
page or link...
*/
return FadeTransition;
};
});
$('.no-click').on("click", function(e) {
e.preventDefault();
});
For instance, this design studio has an animation that runs when you first load the home page, but not after a refresh. (NOTE: this seems to only apply to the mobile version, but it's what I'm trying to achieve. The element I'm animating is present on every page, so making sure it only fires on first load & ONLY on the index.html is something I'm shooting for)
Any help/suggestions/constructive criticism is appreciated!
Code executed on the client is stateless between loads. If you want to remember state from page load to page load, you can either:
Track the session on the back end.
Use cookies/local storage in the client's browser.
You can easily do this from the server side.
Check the referrer header. If it is not present, or it is not the same as the URL in the current request, go ahead and emit the jquery function so that it will execute when the page is loaded into the browser. When it is the same as the URL in the current request, just withhold the jquery script so that it can't run.
If the page is 100% static and you can't do anything like this, well, see Chase's answer.
You can use the transitionCompleted function to load your script.
Barba.Dispatcher.on('transitionCompleted', function(currentStatus, prevStatus) {
if (prevStatus) {
setTimeout(function() {
// call your script
}, 1000);
}
});
Remember that the barba.js event transitionCompleted is fired each time a new page is loaded.

Dealing with detached dom elements, gridster.js plugin

I am working with the gridster.js plugin, which is great, however it seems to be giving me issues when I am removing items using it's remove method. I am using its built in method called remove_all_widgets to wipe out what I currently have on the page and load in new content.
You can see it here.
fn.remove_all_widgets = function(callback) {
this.$widgets.each($.proxy(function(i, el){
this.remove_widget(el, true, callback);
}, this));
return this;
};
It loops through the widgets and calls this remove_widget as seen here:
/**
* Remove a widget from the grid.
*
* #method remove_widget
* #param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
* #param {Boolean|Function} silent If true, widgets below the removed one
* will not move up. If a Function is passed it will be used as callback.
* #param {Function} callback Function executed when the widget is removed.
* #return {Class} Returns the instance of the Gridster Class.
*/
fn.remove_widget = function(el, silent, callback) {
var $el = el instanceof $ ? el : $(el);
var wgd = $el.coords().grid;
// if silent is a function assume it's a callback
if ($.isFunction(silent)) {
callback = silent;
silent = false;
}
this.cells_occupied_by_placeholder = {};
this.$widgets = this.$widgets.not($el);
var $nexts = this.widgets_below($el);
this.remove_from_gridmap(wgd);
$el.fadeOut($.proxy(function() {
$el.remove();
if (!silent) {
$nexts.each($.proxy(function(i, widget) {
this.move_widget_up( $(widget), wgd.size_y );
}, this));
}
this.set_dom_grid_height();
if (callback) {
callback.call(this, el);
}
}, this));
return this;
};
I have small bits of javascript to run button functions and other assorted things. I realized soon after playing with it that it leaves the full html content of the widget in a detached dom tree thus keeping the js files running. I first found this because the buttons have the same names on new pages and it was running the click functions for both the newly loaded button and the one i had taken off the screen using gridsters remove_all_widgets method.
I can track the previous javascript to an (anonymous function) in chomes dev console, and within that i can see the entire html content inside of the detached tree. I am not refreshing the pages or anything, the new content is being brought in by ajax (I set ajax cache:false as well).
Is there any way around this? Would it be possible to clear the contents of the widgets before they get stuck like this? It would be ideal if it didn't happen at all or of there was some way to get rid of them completely when they get removed.
Thank you for taking the time to read this, any insight would be greatly helpful.
As per requests her is some of the code, the click functions on the lines button for example is double firing:
$(document).ready(function() {
$(document).on("change",".accountContries",function(e){
var countryPck = $("body > .addAccountForm1").find(".accountContries").find('option:selected').attr('id');
$.ajax(
{
cache: false,
url : "/listStates/" + countryPck,
type : "GET",
beforeSend: function(){
$("body").append("<div class='loadingNow'></div>");
},
success:function(data, textStatus, jqXHR) {
$('.loadingNow').remove();
$(".accountStates").empty();
$(".accountStates").append("<option value='' selected disabled> Select a State</option>");
$.each(data.states, function(){
$(".accountStates").append("<option value=" + this.id +" id=" + this.id + ">" + this.name +"</option>");
});
},
error: function(jqXHR, textStatus, errorThrown)
{
errorOffScreen("List States by account");
}
});
});
$(document).on("touchend click", ".lines-button", function(e){
e.stopImmediatePropagation();
if($(this).hasClass("close")){
$(this).removeClass("close");
$(".widget1x1Back").next(".actionsHolder3").slideUp("fast", function() {
$(this).remove();
});
}else{
var iconsList = $(this).closest(".top1x1").next(".hdnActnLst").find(".iconsHolder3").html();
$(this).closest(".widget1x1").append(iconsList);
$(this).closest(".widget1x1").find(".actionsHolder3").hide();
$(this).closest(".widget1x1").find(".actionsHolder3").slideDown(700,"easeOutBack");
$(this).addClass("close");
}
});
});
</script>
UPDATE : it seems I only the stuff inside the <Script> tag is being kept even after the elements are .removed
Based on your comments and updates, you have Javascript in the loaded content pages. As that includes the use of delegated event handlers, that code will live on.
This is one case were you would be better off with normal non-delegated event handlers like:
$(".lines-button").bind("touchend click", function(e){
e.stopImmediatePropagation();
These bindings will terminate when the elements are removed (as they are per-element handlers).
An alternative is to not have any code in the content pages, but only in the master page, and apply code as required when loading new content.

Jquery function - on element

I have this jquery code, working 100%.
This is executing on every page load, when i import my js file in the html.
My question, how can i make use of this code, only when the element is present, or call it directly from the element itself? I have similiar functions, but, on html's that dont have specific elements, javascript execution halts. Sometimes because the html elements dont exist on that specific html file.
/**
* Message Box Display
*/
$(document).ready(function() {
$("#Message-Box" ).slideDown("slow", function() {
$("#Message-Box").addClass('Show');
setTimeout(function(){
$('#Message-Box').addClass('Hide');
}, 5000);
});
});
try something like this
$(document).ready(function() {
var msgbx = $("#Message-Box");
if (msgbx.length) {
msgbx.slideDown("slow", function() {
msgbx.addClass('Show');
setTimeout(function() {
msgbx.addClass('Hide');
}, 5000);
});
}
});
The function halting my script was this one:
/**
* Background-Audio
*/
$(document).ready(function() {
var audioTrack = document.getElementById("Background-Audio");
if(audioTrack)
{
audioTrack.volume = 0.05;
audioTrack.play();
}
});

Number, hover (etc) effect not work after Ajax Load More

I'm using Drupal 7 and get my content with View module on page. And my pager Views Load More module. And my thumbnail effect hover, shadow etc. Image hover using this code:
var hoverImg = '<div class="hoverimg"></div>';
$(".thumb").each(function(){
$(this).children("div").each(function(){
$(this).find("a").append(hoverImg);
});
});
$(".thumb div").hover(function() {
$(this).find(".hoverimg").animate({ opacity: 'toggle' });
});
$(".thumb").hover(function() {
$(this).find("div").each(function(){
$(this).find(".shadow").fadeOut(500);
});
});
And getting number on my current thumbnail. This code:
var c = '';
var d = '';
$('.view-content div.views-row').each(function(){
c = 0;
d = 0;
var i = 1;
d = $(this).find('.thumbimg').length;
$(this).find('.thumbimg').each(function(){
sayi=i++;
$(this).append('<div class="img_no">0'+sayi+'</div>');
});
});
Everything is OK. All effects on start page. But when click Load More button, my effects can't work another page.
How do i solve this problem? Thanks.
The reason why it stops working is due to the hover function (and your other scripts/functions) only works on existing elements. So if you add something with ajax, it wont apply to that unless you reload the script after the ajax load.
Another option is to use live() or on() (for the hover part. On is the new version of live, added in jQuery 1.7).
Live and on listens for any existing or future elements.
A live script would look something like this:
$(".yourElement").live({
mouseenter:
function () {
// Do something
},
mouseleave:
function () {
// Do something
},
mousemove:
function () {
// Do something
}
});

Adding delay to jquery event on mouseover

I am trying to add a simple delay to a mouseover event of a child and having difficulties. (Still learning!)
This enables me to show the popup after a delay, but shows all of them simultaneously:
onmouseover='setTimeout(function() { $(\".skinnyPopup\").show(); }, 600)'
and this works to show only the popup I want with no delay:
onmouseover='$(this).children(\".skinnyPopup\").show()'
but the combination does not:
onmouseover='setTimeout(function() { $(this).children(\".skinnyPopup\").show(); }, 600)'
Any help would be appreciated. Thanks!
You need to define what this is when it executes, something like this would work:
setTimeout($.proxy(function() { $(this).children(".skinnyPopup").show(); }, this), 600)
Or just use .delay(), like this:
$(this).children(".skinnyPopup").delay(600).show(0);
Both of the above are quick fixes, I suggest you move away from inline handlers and check out an unobtrusive method (see this answer by Russ Cam for some great reasons), for example:
$(function() {
$('selector').mouseover(function() {
$(this).children(".skinnyPopup").delay(600).show(0);
});
});
It's because this is bound to the global context, not the element. Use something like the following instead:
// put this in your document head -- replace element with a selector for the elements you want
$(function () {
$(element).bind("mouseover", function () {
var e = $(this);
setTimeout(function () { e.children(".skinnyPopup").show(); }, 600);
});
});
If you're adamant about inline event handlers, the following should also work:
onmouseover='var self = this; setTimeout(function() { $(self).children(\".skinnyPopup\").show(); }, 600)'

Categories