jQuery reload function - javascript

Here's what I'm trying to achieve:
Scrolling marquee content (with flexible length) makes a complete journey from right to left of the screen
Once it has disappeared off the screen, bring up some generic messages
In the background during generic messages, check for any new scrolling content and load it
Only when the generic messages have finished displaying, start scrolling again (if there is new content), otherwise repeat the generic messages
http://jsfiddle.net/Vbmm5/
(function($) {
$.fn.marquee = function(options) {
return this.each(function() {
var o = $.extend({}, $.fn.marquee.defaults, options),
$this = $(this),
$marqueeWrapper,
containerWidth,
animationCss,
elWidth;
o = $.extend({}, o, $this.data());
o.gap = o.duplicated ? o.gap : 0;
$this.wrapInner('<div class="js-marquee"></div>');
var $el = $this.find('.js-marquee').css({
'margin-right': o.gap,
'float':'left'
});
if(o.duplicated) {
$el.clone().appendTo($this);
}
$this.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>');
elWidth = $this.find('.js-marquee:first').width() + o.gap;
$marqueeWrapper = $this.find('.js-marquee-wrapper');
containerWidth = $this.width();
o.speed = ((parseInt(elWidth,10) + parseInt(containerWidth,10)) / parseInt(containerWidth,10)) * o.speed;
var animate = function() {
if(!o.duplicated) {
$marqueeWrapper.css('margin-left', o.direction == 'left' ? containerWidth : '-' + elWidth + 'px');
animationCss = { 'margin-left': o.direction == 'left' ? '-' + elWidth + 'px' : containerWidth };
}
else {
$marqueeWrapper.css('margin-left', o.direction == 'left' ? 0 : '-' + elWidth + 'px');
animationCss = { 'margin-left': o.direction == 'left' ? '-' + elWidth + 'px' : 0 };
}
$marqueeWrapper.animate(animationCss, o.speed , 'linear', function(){
getUpdates();
});
};
setTimeout(animate, o.delayBeforeStart);
});
};
})(jQuery);
$(function(){
$('#scrollerContent').marquee({
speed: 3000,
gap: 50,
delayBeforeStart: 0,
direction: 'right',
duplicated: false,
pauseOnHover: false,
});
});
function getUpdates()
{
alert("Hello"); // This is where the jQuery get function would be to update the text
alert("Show Details"); // This is where the generic details would be displayed
marquee();
}
The problem is the scrolling element requires a width, which obviously changes with every new 'load' of messages. I tried putting the getUpdates() function inside the main jQuery function, which does work almost perfectly but doesn't update the containerWidth variable, so messages longer than the original start half-way through, and shorter messages take ages to appear.
What I need is for the whole of the function to be re-run, including the re-setting of the width after the #scrollerText paragraph has been changed.
How do I do this?

If you had used console.log() instead of alert() you would have had the console open and seen
Uncaught ReferenceError: marquee is not defined
In getUpdates() you're calling a function marquee(); that does not exist. The script terminates there.
Go back a few steps (undoing what you've removed) and where the code triggers the animation, add the code to update the text before that, or if you're getting data you need to wrap that bit of code.
So, if you were getting data from the server, theurl.php would return text new text and nothing else. Move the code that triggers the animation to go again within the $.get callback function.
http://jsfiddle.net/Vbmm5/4/
$marqueeWrapper.animate(animationCss, o.speed , 'linear', function(){
// clear the text to prevent it from hanging at the end of the
// marquee while the script gets new data from the server
$this.find('#scrollerText').text('');
// get new text
$.get('theurl.php', function(response){
$this.find('#scrollerText').text(response);
// update the width
elWidth = $this.find('.js-marquee:first').width();
//fire event
$this.trigger('finished');
//animate again
if(o.pauseOnCycle) {
setTimeout(animate, o.delayBeforeStart);
}
else {
animate();
}
});
});
(the URL and post data in the example on jsfiddle is jsfiddle's way of returning html)
I've used $this.find('#scrollerText').text(response); even though there should be only one id and $('#scrollerText').text(response); would be fine. If you were to have multiple marquees you would target each marquee's text using $this.find, so if you want more than one use classes instead $this.find('.scrollerText').text(response);

Related

Execute something while element is in view

I am using the Jquery inview plugin and I am trying to load some elements whenever the user reached the footer of the page. While doing this, I discovered a bug where if the user holds the scroll-click and drags the mouse towards the bottom, in some cases the elements will not load anymore until the footer is out of the view and then back into the view.
Here is the function that I have so far to load the elements when the footer is in the viewport:
//Infinite load function. Uses jquery.inview
$scope.addMoreElements = function(){
$scope.limitElementsPerPage += 16;
$('.footer').on('inview', function(event, isInView) {
if (isInView) {
// element is now visible in the viewport
$scope.limitElementsPerPage += 16;
} else {
// element has gone out of viewport
//do nothing
}
});
};
I am using Angularjs as well as jQuery for this project. Essentially, what I think I need is something that checks at about 1-2 seconds if the element is still in view. I am not exactly sure I should do this at the moment. This is what I tried to do to solve this issue:
$scope.$watch($('.footer'), function(){
$('.footer').on('inview', function(event, isInView) {
setTimeout(function(){
while(isInView){
console.log('test')
}
}, 1000);
});
});
This unfortunately, will crash the browser (I am not sure how I would go about doing this with the setTimeout or the other related functions).
Any help or ideas on how to do this would be greatly appreciated.
Thank you in advance.
InView adds a new event for elements, that triggers when the element enters the viewport. Probably some times you just have the footer in the viewport at all times, so that is why it fails.
I think you need to redesign the logic of the page to use the 'scroll' event on whatever element contains the added items and scrolls for the infinite view and in that event to check if the footer is in the viewport, not if it enters.
Personally I use this extension for checking if it is in the viewport:
(function($) {
$.inviewport = function(element, settings) {
var wh=$(window).height();
var wst=$(window).scrollTop();
var et=$(element).offset().top;
var eh=$(element).height();
return !(wh + wst <= et)&&!(wst >= et + eh);
};
$.extend($.expr[':'], {
"in-viewport": function(a, i, m) {
return $.inviewport(a);
}
});
})(jQuery);
Here are couple of functions you can use:
var getScrollY = function(){
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ?
document.documentElement.scrollTop : document.body.scrollTop;
return y;
}
function get_elem_y( elem ) {
var box = elem.getBoundingClientRect();
return box.top + getScrollY();
}
And then you can listen to the scroll event, assume footer is something like <div id="footer">...</div>
var footer = document.getElementById("footer"); // get footer
var b_foot_visible = false;
window.addEventListener("scroll", function() {
var y = get_elem_y(footer);
var pageHeight = ( window.innerHeight || document.body.clientHeight);
if((getScrollY() + pageHeight) > y ) {
// footer is visible
if(!b_foot_visible) {
// TODO: add something
b_foot_visible = true;
}
} else {
// footer is not visible
if(b_foot_visible) {
// TODO: remove something
b_foot_visible = false;
}
}
});
Thus, when the scrollY + pages height reaches the footer elements Y coordinate you can do something to display things for the footer.
You might also add check in the beginning to test if the footer is already visible.

Run casper.evaluate() in included / external script

Introduction
I am using casperJS together with grunt-casper plugin for automating Tests for our new GUI Framework and just have a question about running jQuery / JS Code in the casper.evaluate() function to point out elements for capturing them.
First steps creating functionality
At first i build the tests a described in the casperJS HowTo like this:
casper.test.begin('LOGIN FORM TEST', function(test) {
casper.start('http://www.sample.org');
casper.waitForSelector("form.login-box",
function success() {
casper.test.assertElementCount('form.login-box > .well', 3, 'WORKS');
},
function fail() {
/* Create overlay over malicious element */
casper.evaluate(function () {
/* Get boundaries of malicous element */
var buffer = 6;
var height = $(selector).height() + buffer + "px";
var width = $(selector).width() + "px";
var position = $(selector).offset();
var posX = position.left + "px";
var posY = position.top + buffer + "px";
/* Add a overlay which matches the element and overlays it */
$("body").append("<div id='errormarker'></div>");
$("#errormarker")
.css({
'opacity': 0.4,
'position': 'absolute',
'top': posY,
'left': posX,
'height': height,
'width': width,
'background-color': '#f00',
'z-index': 5000
});
});
casper.test.fail('NOT COOL');
}, 200);
casper.capture('image.png');
casper.run(function() {
test.done();});
});
This worked fine, at first i created a DIV which has the measurements and position of the malicious element. Could be verified on the screenshot.
Second step make it a general UtilFunction
But as i have a lot of tests which need this functionality for taking a screenshot when a test fails, i assumed to create a Utils.js, include it to the script and call the function with parameters whenever i need it so i created this:
Gruntfile (because i use grunt-casper the include of the script is here, its just a simple include nothing specific)
casper: {
MyTest: {
options: {
includes: ['tests/testutils/Testutils.js']
Testutils.js
/**
* Constructor of TestingUtils
* #constructor
*/
function Testutils() {}
/**
* Function for creating a Screenshot with a marked malicious element for logging erroneous created objects
* #param selector CSS selector of the element
* #param pathForScreenshot the path where the screenshots are stored
* #param casper the casper test object
* #param screenshotName the name of the Screenshot which has to be created
*/
Testutils.prototype.createErrorScreenshot = function (selector, pathForScreenshot, casper, screenshotName) {
/* Set thin border around malicous element */
casper.evaluate(function () {
/* Get boundaries of malicous element */
var buffer = 6;
var height = $(selector).height() + buffer + "px";
var width = $(selector).width() + "px";
var position = $(selector).offset();
var posX = position.left + "px";
var posY = position.top + buffer + "px";
/* Add a overlay which matches the element and overlays it */
$("body").append("<div id='errormarker'></div>");
$("#errormarker")
.css({
'opacity': 0.4,
'position': 'absolute',
'top': posY,
'left': posX,
'height': height,
'width': width,
'background-color': '#f00',
'z-index': 5000
});
});
/* screenshot the whole screen with marker element */
casper.capture(pathForScreenshot + screenshotName);
/* Cleanup the content from marker */
casper.evaluate(function () {
$('#errormarker').remove();
});
/* Create specific screenshot of the element */
casper.captureSelector(pathForScreenshot + screenshotName, selector);
};
Calling the Function in Test.js
casper.test.begin('LOGIN FORM TEST', function(test) {
casper.start('http://www.sample.org');
casper.waitForSelector("form.login-box",
function success() {
casper.test.assertElementCount('form.login-box > .well', 3, 'WORKS');
},
function fail() {
/* THIS HERE IS NEW */
var testutils = new Testutils();
actUtils.createErrorScreenshot('form.login-box > .well > .form-group:nth-child(1)', tempCaptureFolder, casper, 'image.png');
});
casper.test.fail('NOT COOL');
}, 200);
casper.capture('image.png');
casper.run(function() {
test.done();});
});
Problem now
The casper specific functions (casper.capture) work in the included js file, BUT casper.evaluate is not run....never, i debugged and logged this but i cannot use this functionality here.
So my question is, what can i do here? I need to use jQuery functionality here and especially evaluate for marking the DOM content before screenshotting it.
I think it has to do with the following mechanism how evaluate works:
http://docs.casperjs.org/en/1.1-beta2/_images/evaluate-diagram.png
I would be very very glad if someone can help me here.
Next steps
I now went on and no errors are provided. Another problem occurred and i am wondering what happens here.
But now magically casper.evaluate is entered, but i got errors that the parameters
var height = $(selector).height() + buffer + "px";
var width = $(selector).width() + "px";
var position = $(selector).offset();
var posX = position.left + "px";
var posY = position.top + buffer + "px";
could not be initialised, o.e. the $(selector) returned NULL, so i tried to get the HTML context and when i got the DOM with jQuery i got the following output:
<html><head></head><body></body></html>
so the content is empty.
SSL Problem?
Now i know the problem with SSL and Casper and as i said when i ran the casper.evaluate in the function calling script it worked fine because i set the params
args: ['--ssl-protocol=any', '--ignore-ssl-errors=true', '--web-security=no']
in GRUNT configuration.
Wrong page?
Now i thought that i am on the wrong website so i got the URL from casper.getCurrentUrl() and it is the the correct URL, so i took a capture inside of the evaluate() function, and the screenshot was correct too what proved that i am on the correct page.
But as i said the gotten HTML Content is empty, so what can i do here?
I think it must be a small problem, maybe with the sandboxed content, i do not have a concrete idea here.

Different height of element depending on page load

i'm developing a site where i use jQuery to achieve a faux columns effect. Here is a test page: http://goo.gl/IL3ZB . The left yellow <aside> height is set in java script with the height of the .body_container div. The height is set correctly for display.
The problem is when i do in Firefox 17 a full refresh (Shift + F5) the <aside> is displayed correctly, with the correct height, but the animation in js sees a much smaller height. When i then refresh the page normally, then java script also sees the correct height.
How can i resolve this problem?
Here is my js:
var floating_patents_bottom = 0;
$(window).load(function(){
$('.floating_patents').height( $('.body_container').height() );
floating_patents_bottom = ($('.body_container').height() > floating_patents_bottom ? $('.body_container').height() : floating_patents_bottom);
var toBottom = {
'top': floating_patents_bottom
};
});
var toTop = {
'position': 'absolute',
'top': '500px',
'display': 'none'
};
$(document).ready(function(){
$('.floating_patents').height( $('.body_container').height() );
floating_patents_bottom = ($('.body_container').height() > floating_patents_bottom ? $('.body_container').height() : floating_patents_bottom);
// floating_patents_bottom = $('.floating_patents').height();
var toBottom = {
'top': floating_patents_bottom
};
var patents = $(".floating_patents img");
patents.css(toTop);
patents.each(function(index) {
$(this).delay(index * 5000).css('margin','10px auto').fadeIn("slow").animate(toBottom , 15000, function(){
$(this).fadeOut("slow");
});
});
});
The problem is that when handler $(document).ready is called your images in content aren't fully loaded and have zero dimensions, so your $('.body_container').height() calculated incorrectly (the calculations sometimes happens correctly when browser takes images from the cache). The easiest solution for you is to move all code inside $(window).load handler.
A little refactored code which will work:
function floatingPatents() {
// find required elements in DOM
var patentsBlock = $('.floating_patents'), bodyContainer = $('.body_container');
var patents = patentsBlock.find('img').hide();
var floating_patents_bottom = 0;
// wait for complete page load
$(window).load(function(){
// resize holder
floating_patents_bottom = bodyContainer.height();
patentsBlock.height( floating_patents_bottom );
// calculate offsets
var toTop = {
position: 'absolute',
top: '500px',
display: 'none'
};
var toBottom = {
top: floating_patents_bottom
};
// start animation
patents.show().css(toTop).each(function(index) {
$(this).delay(index * 5000).css('margin','10px auto').fadeIn("slow").animate(toBottom , 15000, function(){
$(this).fadeOut("slow");
});
});
});
}
// run code when page ready
$(floatingPatents);
The document is ready before all of its elements are loaded. You're getting the correct height on the $(window).load event, but you're initializing the animations in the $(document).ready event. Just move everything into $(window).load and you should be good.
If waiting for the window to finish loading is too long (since otherwise, you won't be able to get the proper height of your .body-container div), you might be able to try this technique for getting placeholders for your images, so that the flow is correct before they've actually loaded.
http://andmag.se/2012/10/responsive-images-how-to-prevent-reflow/

problems setting css with jquery

I have created a slideshow with jquery. It clones an image in a container, moves it to the right, then slides it to the left and starts over. Here is the code:
$(document).ready(function() {
var slideshow = new main.slideshow();
slideshow.start({
path: 'images/slideshow/',
images: ['1', '2']
});
});
var main = new (function() {
this.slideshow = (function() {
var self = this;
var nextSlide, path, images, startLeft;
var fileExtension = 'jpg';
var container = $('#slideshow');
var currentSlide = container.children('img');
var timerlength = 4000;
var currentSlideIndex = 0;
this.start = function(args) {
path = args['path'];
images = args['images'];
if (typeof args['fileExtension'] !== 'undefined') fileExtension = args['fileExtension'];
container.css('overflow', 'hidden');
currentSlide.css('position', 'absolute');
startLeft = currentSlide.position();
startLeft = startLeft.left;
self.nextSlide();
};
this.nextSlide = function() {
nextSlide = currentSlide.clone();
nextSlide.css('left', (startLeft + currentSlide.width()) + 'px');
currentSlideIndex++;
if (currentSlideIndex >= images.length) currentSlideIndex = 0;
nextSlide.attr('src', path + images[currentSlideIndex] + '.' + fileExtension);
container.append(nextSlide);
setTimeout(function() {
self.slideToNext();
}, timerlength);
};
this.slideToNext = function() {
currentSlide.animate({
left: '-' + (currentSlide.width() - startLeft) + 'px'
}, 2000);
nextSlide.animate({
left: startLeft + 'px'
}, 2000, function() {
currentSlide.remove();
currentSlide = nextSlide;
self.nextSlide();
});
};
});
});
A link to see this in action can be found here:
https://dustinhendricks.com/breastfest/public_html/
The problem I'm having as you can see is that the second slide when first added to the dom, does not seem to be moved to the right when I call css('left', x);. After the first jQuery animation however, each cloned slide then seems to be able to be moved to the right with that call no problem. This leads me to believe that jquery's animate is setting something that allows for the object to be moved via css('left', x);, but what could it be changing? position is already being set to absolute.
This is why my example pages seems to take a while before the slides start sliding. Any idea how I can fix?
If your first image is not loaded yet when you call .start() such that currentslide.width() isn't correct, then it won't set the proper initial value for left upon initialization. You may need to set a .load() event handler so you know when that first slide is loaded and you can wait for it to be loaded before starting the slideshow.
When testing this, you must set the .load() handler before the .src value is set on the image object (or you may miss the load event in IE) and you should make sure you test both the cases where no images are cached and where all images are cached (as the timing of the load event can be different in both cases).

How to reset to original values?

It looks like it keeps adding a new newHeight and a newDistance each time i click, I am trying to save original height with a global var at the top and using data to do that but i get weird results, basically i should be able to reset newDistance and newHeight to first original values as per before to run the lot with a click but it doesn't and i get new added values each time i click breaking my layout as a result:
talents = $(".talenti");
filter = $(".filtra");
genHeight = $("#container").data($("#container").height());
filter.click(function(e) {
e.preventDefault();
if (talents.hasClass("opened")) {
$(".nasco").slideToggle();
$("#wrapNav").slideToggle("10", "linear");
talents.removeClass('opened');
filter.addClass('opened');
$("#container").css("height", genHeight);
} else {
filter.addClass('opened');
};
if (filter.hasClass("opened")) {
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").slideToggle();
var newHeight = $("#container").height() + $("#wrapNav").outerHeight(true);
var newDistance = newHeight - $("#container").height() + 22;
$("#container").animate({height: newHeight}, 50,function(){
$(".box").animate({top: newDistance});
});
});
}
});
talents.click(function(e) {
e.preventDefault();
if (filter.hasClass("opened")) {
$("#sliding-navigation").slideToggle();
$("#wrapNav").slideToggle("10", "linear");
filter.removeClass('opened');
talents.addClass('opened');
$("#container").css("height", genHeight);
} else {
talens.addClass('opened');
};
if (talents.hasClass("opened")) {
$("#wrapNav").slideToggle("10", "linear", function(){
$(".nasco").slideToggle();
var newHeight = $("#container").height() + $("#wrapNav").outerHeight(true);
var newDistance = newHeight - $("#container").height() + 156;
$("#container").animate({height: newHeight}, 50,function(){
$(".box").animate({top: newDistance});
});
});
}
});
Anyone?
So, based on the code I could download about 20min ago from your test site, I managed to get it working with the following code:
$(document).ready(function(){
// placeholder to contain the original height...
var original_height = 0;
talents = $(".talenti");
filter = $(".filtra");
filter.click(function(e){
e.preventDefault();
if (filter.hasClass('opened')){
filter.removeClass('opened');
// toggle the wrapping, just with a zero top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").hide();
$(".box").animate({top: '0px'});
});
// reset to the original height...
$("#container").height(original_height);
}
else {
// get the original height if it's not already set...
if (original_height == 0)
original_height = $("#container").height();
filter.addClass('opened');
if (talents.hasClass("opened"))
{
$(".nasco").hide();
$("#wrapNav").slideToggle();
talents.removeClass('opened');
}
// toggle the wrapping with a height of the nav as top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").slideToggle(true, function(){
// need the height of the nav before we know how far to move the boxes...
var newHeight = $("#wrapNav").outerHeight(true);
$(".box").animate({top: newHeight});
// set the container's new height, much like you had...
$("#container").height(original_height + newHeight);
});
});
}
});
talents.click(function(e) {
e.preventDefault();
if (talents.hasClass('opened')) {
talents.removeClass('opened');
// toggle the wrapping, just with a zero top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$(".nasco").hide();
$(".box").animate({top: '0px'});
});
// reset to the original height...
$("#container").height(original_height);
}
else {
// get the original height if it's not already set...
if (original_height == 0)
original_height = $("#container").height();
talents.addClass('opened');
if (filter.hasClass("opened"))
{
$("#sliding-navigation").hide();
$("#wrapNav").slideToggle();
filter.removeClass('opened');
}
// toggle the wrapping with a height of the nav as top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
// need the height of the nav before we know how far to move the boxes...
$(".nasco").slideToggle(true, function(){
var newHeight = $("#wrapNav").outerHeight(true);
$(".box").animate({top: newHeight});
// set the container's new height, much like you had...
$("#container").height(original_height + newHeight);
});
});
}
});
});
A few points adding food for thought:
I simplified the multiple if statements to make it easier to understand and process
I used hide() to avoid messy animation problems if you clicked on FILTER multiple times in a row
I only adjusted the top coordinates of the boxes to achieve this
I would have preferred to contain the boxes in a more general container, allowing for easier animation and management, but I understand that wordpress doesn't always give you the most room to work, so this should get you on your way!
It might not be completely what you're looking for in your animation, but it's a working example of the code you had and should get you 90% of the way...hope this helps! :)
What about using the data collection of the container element rather than a global variable i.e. at the top record the height
$("#container").data('height', $("#container").height());
then to use
$("#container").data('height');
i.e. to reset the height
$("#container").css({height: $("#container").data('height') });
I feel a bit suspicious about how the global variable is working. Worth a try maybe

Categories

Resources