iOS unable to manipulate jQuery DOM changes on the first page load - javascript

I've been having a weird issue with Safari on iOS.
I'm using jQuery to manipulate a couple of divs, moving them, wrapping them and giving them dynamic heights. On every browser, these changes are perfectly working during the first page load, on Safari, sometimes yes and sometimes not.
basically in Safari, it loads the page without applying any changes, then if I keep refreshing it, it never works, but if I reenter the url, on the address bar and I hit "enter", that time it works.
Now, I solved it wrapping the function that triggers those DOM manipulations in a setTimeout function and I give it a delay of 400. SO now it waits 400ms before firing the function, and this way it's working, but I don't like it that much. I was wondering if you guys know a better system.
my code:
$('.wrap-services, .wrap-banner-cta, .wrap-logos, footer').wrapAll('<div class="fixer"></div>');
$('.fixer').insertAfter('.content');
function getHeight() {
var imgOffset = $('.wrap-hero-home picture.splash-main img').offset().top;
var fixerHeight = $('.fixer').height();
imgHeight = $('.wrap-hero-home picture.splash-main img').height();
$('.fixer').css('top', (imgOffset + imgHeight) + 50);
$('.container').height((imgOffset + imgHeight + fixerHeight) + 50);
}
//this is what I use now to prevent that issue
setTimeout(getHeight, 400);
Thanks

Technically, jQuery's bind method is deprecated, so you should probably use on, like they suggest:
$(window).on('load', function(){
//Do something
});
See the notes in this page related to the deprecation: http://api.jquery.com/bind/
If you're using an old version of jQuery, I guess it wouldn't matter, then proceed with bind

Related

jQuery detect element resize

I am trying to do detect element resize with jquery resize plugin (http://benalman.com/projects/jquery-resize-plugin/) on jquery 1.10.2.
$("#element").resize(function(){
console.log("resize");
});
I did the testing on Firefox 25 and I get this error:
Error: TypeError: r is undefined
Source File: jquery.ba-resize.min.js
Line: 9
How can I solve it? Is there any alternative way / plugin for doing this?
Thank you.
You need a resize sensor which is bundled with css-element-queries of https://github.com/marcj/css-element-queries.
new ResizeSensor($('.elements'), function(){
console.log('resize')
});
jQuery's .resize only works on window object since only window has a event onresize. All other element haven't and thus you need a polyfill for that. I've seen a lot of other jQuery plugins that allow you to listen on resize changes of all element types, but take care: These are incredible slow as they use setTimeout() or interval to check it's dimension change instead of setting up a real resize sensor like the one you can find in the link above.
Using Clay.js (https://github.com/zzarcon/clay) it's quite simple to detect changes on element size:
var el = new Clay('.element');
el.on('resize', function(size) {
console.log(size.height, size.width);
});
DIV does not fire a resize event, so you won't be able to do exactly what you've coded, but you could look into monitoring DOM properties.
If you are actually working with something like resizables, and that is the only way for a div to change in size, then your resize plugin will probably be implementing a callback of its own.
Remove that plug-in, jQuery done with it self.
var element = $('#element');
var current_size = element.width()+'x'+element.height();
element.resize(function(){
var this_size = $(this).width()+'x'+$(this).height();
if(current_size!==this_size){
console.log('changed');
current_size = this_size;
}
});
PS : Doesn't test

jQuery infinite-scroll Not Triggering

I'm making a simple little website to apply a different formatting style to Reddit posts, I'm trying to add the infinite-scroll jQuery plugin but it doesn't do anything. I tried following the (very simple) instructions on the infinite-scroll page and when it didn't do anything I thought I must have entered something wrongly, but then I just copy/pasted the code from the Masonry/Infinite-Scroll example and it still didn't work. Masonry is working perfectly (finally) but I just can't figure out what is wrong with infinite-scroll. I understand the basics of jQuery and JavaScript, but obviously not as much as most of you people, so could you please help me out and let me know what is wrong? My site is live at reddit.ymindustries.com.
Thanks heaps, you guys have rarely failed me so far.
YM
EDIT: If there aren't enough images to fill up the page on the homepage, visit reddit.ymindustries.com/r/aww for more images.
EDIT 2: I believe I located the issue, it is described here: https://github.com/paulirish/infinite-scroll/issues/5
Now to figure out a fix...
EDIT 3: Added a little bit of a hack in to make it sort of work, but it just seems to loop the second page endlessly now. Hmm...
I think your problem is actually css. Make your page longer that client area height. add more images to $container
Point is, botom edge of your $container need to pass bottom of window so scroll event fires so infinite scroll can react on this event and calculate weather or not edge is reached
BTW, in same cases, for instance, when I shrink my window, the example you set is working.
=== UPDATE ===
I found some time to play with infinitescroll and here is final working script, just set pathParse method in your script
$(function () {
var $container = $('#itemContainer');
$container.imagesLoaded(function () {
$container.masonry({
itemSelector:'.item'
});
});
$container.infinitescroll({
navSelector:'.navigation', // selector for the paged navigation
nextSelector:'.navigation #next', // selector for the NEXT link (to page 2)
itemSelector:'.item', // selector for all items you'll retrieve
bufferPx:40,
debug:true,
columnWidth:function (containerWidth) {
return containerWidth / 5;
},
loading:{
finishedMsg:'No more pages to load.',
img:'http://i.imgur.com/6RMhx.gif'
},
pathParse: function(path,page){
return $(this.nextSelector).attr("href");
}
},
// trigger Masonry as a callback
function (newElements) {
// hide new items while they are loading
var $newElems = $(newElements).css({ opacity:0 });
// ensure that images load before adding to masonry layout
$newElems.imagesLoaded(function () {
// show elems now they're ready
$newElems.animate({ opacity:1 });
$container.masonry('appended', $newElems, true);
});
//console.log("test (never fired :( )");
}
);
});
Now, since your next link will not update by it self (http://reddit.ymindustries.com/?after=t3_yh4av), you need to change the callback to pull out last element from ajax response and change next link... could be something like this
function (newElements) {
// hide new items while they are loading
var $newElems = $(newElements).css({ opacity:0 });
// ensure that images load before adding to masonry layout
// ======> if query parameter after=... is caring filename then do this
var lastImageUrl= $newElements[$newElements.length-1].attr("src");
var lastFileName= lastImageUrl.substring(lastImageUrl.lastIndexOf("/") +1, lastImageUrl.lastIndexOf("."));
$("#next").attr("href", "http://reddit.ymindustries.com/?after="+lastFileName);
$newElems.imagesLoaded(function () {
// show elems now they're ready
$newElems.animate({ opacity:1 });
$container.masonry('appended', $newElems, true);
});
//console.log("test (never fired :( )");
}
You also need to take care of wich version of infinite-scroll your using since if you use the ones that comes with masonry/isotope (version 2.0b2.110713), both need a little hack in order to call the function and not use the predefined array:
//old code, to be changed (line 489)
desturl = path.join(opts.state.currPage);
// new code
desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
This is already fixed in the newer versions of infinite-scroll
I had the same problem with jQuery's "infinitescroll" and Masonry. You might just solve this by giving your page more initial items so that the plugin's scrolling detection kicks in.
In WordPress this is under the "Reading" settings. By default WordPress only opens 10 items at a time. You could increase that number to 100/page to be more sure the window will be full initially. I had some code here that was just horrible, turns out I just needed longer pages, not more code.
So it's difficult to test these plugins on large displays if you don't have enough images. Maybe the solution is to scale the images larger on large displays so you're more sure about getting your content below the fold.
If you think someone might get to your website with a really huge display, I'm not sure what the answer is other than showing more items/page and maybe adding $('#masonry').infinitescroll('retrieve'); to your footer to load an extra page just in case.

Overriding yellow autofill background

After a long struggle, I've finally found the only way to clear autofill styling in every browser:
$('input').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
However, I can’t just run this in the window load event; autofill applies sometime after that. Right now I’m using a 100ms delay as a workaround:
// Kill autofill styles
$(window).on({
load: function() {
setTimeout(function() {
$('.text').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
}, 100);
}
});
and that seems safe on even the slowest of systems, but it’s really not elegant. Is there some kind of reliable event or check I can make to see if the autofill is complete, or a cross-browser way to fully override its styles?
If you're using Chrome or Safari, you can use the input:-webkit-autofill CSS selector to get the autofilled fields.
Example detection code:
setInterval(function() {
var autofilled = document.querySelectorAll('input:-webkit-autofill');
// do something with the elements...
}, 500);
There's a bug open over at http://code.google.com/p/chromium/issues/detail?id=46543#c22 relating to this, it looks like it might (should) eventually be possible to just write over the default styling with an !important selector, which would be the most elegant solution. The code would be something like:
input {
background-color: #FFF !important;
}
For now though the bug is still open and it seems like your hackish solution is the only solution for Chrome, however a) the solution for Chrome doesn't need setTimeout and b) it seems like Firefox might respect the !important flag or some sort of CSS selector with high priority as described in Override browser form-filling and input highlighting with HTML/CSS. Does this help?
I propose you avoiding the autofill in first place, instead of trying to trick the browser
<form autocomplete="off">
More information: http://www.whatwg.org/specs/web-forms/current-work/#the-autocomplete
If you want to keep the autofill behaviour but change the styling, maybe you can do something like this (jQuery):
$(document).ready(function() {
$("input[type='text']").css('background-color', 'white');
});
$(window).load(function()
{
if ($('input:-webkit-autofill'))
{
$('input:-webkit-autofill').each(function()
{
$(this).replaceWith($(this).clone(true,true));
});
// RE-INITIALIZE VARIABLES HERE IF YOU SET JQUERY OBJECT'S TO VAR FOR FASTER PROCESSING
}
});
I noticed that the jQuery solution you posted does not copy attached events. The method I have posted works for jQuery 1.5+ and should be the preferred solution as it retains the attached events for each object. If you have a solution to loop through all initialized variables and re-initialize them then a full 100% working jQuery solution would be available, otherwise you have to re-initialize set variables as needed.
for example you do: var foo = $('#foo');
then you would have to call: foo=$('#foo');
because the original element was removed and a clone now exists in its place.

Code won't execute in $(document).ready but will in developer console

I have some code wrapped in $(document).ready(function(){ /*code*/ });, and all of it works fine, except for one line. The code above it works fine, the code below it works fine, I'm not getting any errors in my console.
$('.main-right.category').height( $('.footer').height() + $('.main-right.category').height() );
That doesn't fire. However, if I paste that exactly in the developer console and press enter after the page has loaded, it works. All of the elements exist at page load (meaning none are built dynamically via javascript). Same result in chrome, firefox, IE.
Any ideas?
edit: I should add that my css is loaded before my javascript, and I've done other CSS related tweaks in this same javascript file that have worked fine.
Also, if I console.log $('.main-right.category').height() and $('.footer').height() right above that line of code, they both give non-zero integer values like I'd expect.
The ready event fires when the DOM is ready to work with. It differs from the load event which fires when all assets (css, javascript, images, ...) are all loaded.
I guess that when you code runs, the elements you're trying to get the height does have an height calculated already so it seems nothing happens.
When you executed your code in the console, everything is loaded so the behavior is the one expected.
To bind to the load event, check the method .load().
$(document).ready fires when the DOM-structure is full available, at this time the rendering ususally isn't finished, so the dimensions of the elements may be unknown and height() will return wrong values.
Use $(window).load() instead.
i usually set height with:
var height = $('.footer').height() + $('.main-right.category').height();
$('.main-right.category').css('height',height+'px');
You should use the console to debug the selectors and view the heights of the elements;
$(document).ready(function() {
var $footer = $('.footer');
var $category = ('.main-right.category');
console.log($category, $footer);
console.log($category.height(), $footer.height());
console.log('New height =', ($category.height() + $footer.height()));
});

How can I scroll to a page element in jQuery Mobile?

I have a long jQuery mobile page and would like to scroll to an element halfway down this page after the page loads.
So far I've tried a few things, the most successful being:
jQuery(document).bind("mobileinit", function() {
var target;
// if there's an element with id 'current_user'
if ($("#current_user").length > 0) {
// find this element's offset position
target = $("#current_user").get(0).offsetTop;
// scroll the page to that position
return $.mobile.silentScroll(target);
}
});
This works but then the page position is reset when the DOM is fully loaded. Can anyone suggest a better approach?
Thanks
A bit late, but I think I have a reliable solution with no need for setTimeout(). After a quick look into the code, it seems that JQM 1.2.0 issues a silentScroll(0) on window.load for chromeless viewport on iOS. See jquery.mobile-1.2.0.js, line 9145:
// window load event
// hide iOS browser chrome on load
$window.load( $.mobile.silentScroll );
What happens is that this conflicts with applicative calls to silentScroll(). Called too early, the framework scrolls back to top. Called too late, the UI flashes.
The solution is to bind a one-shot handler to the 'silentscroll' event that calls window.scrollTo() directly (silentScroll() is little more than an asynchronous window.scrollTo() anyway). That way, we capture the first JQM-issued silentScroll(0) and scroll to our position immediately.
For example, here is the code I use for deep linking to named elements (be sure to disable ajax load on inbound links with data-ajax="false"). Known anchor names are #unread and #p<ID>. The header is fixed and uses the #header ID.
$(document).bind('pageshow',function(e) {
var $anchor;
console.log("location.hash="+location.hash);
if (location.hash == "#unread" || location.hash.substr(0,2) == "#p") {
// Use anchor name as ID for the element to scroll to.
$anchor = $(location.hash);
}
if ($anchor) {
// Get y pos of anchor element.
var pos = $anchor.offset().top;
// Our header is fixed so offset pos by height.
pos -= $('#header').outerHeight();
// Don't use silentScroll() as it interferes with the automatic
// silentScroll(0) call done by JQM on page load. Instead, register
// a one-shot 'silentscroll' handler that performs a plain
// window.scrollTo() afterward.
$(document).bind('silentscroll',function(e,data) {
$(this).unbind(e);
window.scrollTo(0, pos);
});
}
});
No more UI flashes, and it seems to work reliably.
The event you're looking for is "pageshow".
I was digging a lot this issue, also at jQuery mobile official forum.
Currently it seems that there is no solution (at least for me).
I tried different events (mobileinit, pageshow) and different functions (silentscroll, scrolltop) as suggested above, but, as a result, I always have page scrolled until all images and html is finished loading, when page is scrolled to top again!
Partial and not really efficient solution is using a timer as suggested in comment to sgliser's answer; unfortunately with a timeout is difficult to know when page will be fully loaded and if scroll happened before that, it will scroll back to top at the end of load, while if it happens too long after page has fully loaded, the user is already scrolling page manually, and further automated scroll will create confusion.
Additionally, would be useful to have silentscroll or other function to address a specific id or class and not plain pixels, because with different browsers, resolutions and devices it may give different and not correct positioning of the scroll.
Hope someone will find a smarter and more efficient solution than this.

Categories

Resources