I use Twitter Bootstrap and jQuery Masonry for a new site in developement. I append new elements to the container of masonry.
The elements are neither appended or prepended to existing children but in inserted in between them, depending on a sorting order like this:
var elem = $(boxes[rand2]);
$(".post").each(function(i){
if(parseFloat($(this).data("weight"))<=weight){
elem.insertBefore(this);
return false;
}
else if (i == $(".post").length - 1) {
elem.insertAfter(this);
return false;
}
});
This works perfect so far. What I'd like to achieve is that the elements only display when they reached their destination place. I don't want to disable animations, but the space for the new element should appear (empty) and only when everything is re-arranged through masonry the new element should appear.
I thought there would be a callback-action that fires after reload like this:
$('#posts').masonry('reload',function(){
alert('re-aligning finished');
});
but that doesn't work, it fires to early.
So for the moment, I did
$('#posts').masonry('reload',function(){
window.setTimeout(showElem,500);
});
function showPosts(){
$('.post').show();
}
and that seems to work - but a fixed 500ms timeout is not a real beautiful solution...
So, any suggestions?
I'm guessing you're already using jQuery. Perhaps the use of deferred objects could be useful.
Could you do it later after document loads?
(function($) {
$(document).ready(documentReadyFunction);
$(window).resize(windowResizeFunction);
$(window).load(windowLoadFunction);
function documentReadyFunction() {
//your inserted html here
}
function windowResizeFunction() {
//resize here
}
function windowLoadFunction() {
//do masonry here
}
})(jQuery);
Related
I'm using Search & Filter pro WP plugin for the ease of a client using it.
I've created a results page and filter on a demo site (for testing) that works fine but I know the categories will get large on the real site. So I turned the plugins' filters into an Accordion style list.
It works fine until certain searches reload all those filter results with AJAX and they remove my event listeners (which are sitting on elements for the moment, I know it's not ideal but for now I just want to see if it could work).
I imagine because my script has already been parsed when the DOM loaded, the AJAX from the plugin is just redefining those elements and they are then missing the Event Listeners or something.
Any help would be appreciated.
Here's my script:
<?php
add_action( 'wp_footer', function () { ?>
<script>
const clicker = document.querySelectorAll('#search-filter-form-4346 > ul > li > h4');
// looping through the <h4> elements and adding an event listener onto each, the class toggle just adds an animation to a pseudo-element spinner
for (let i = 0; i < clicker.length; i++) {
clicker[i].addEventListener("click", function() {
this.classList.toggle("open-filter-dropdown");
console.log('EL was created');
// declaring the <ul> as a variable
const openFilterPanel = this.nextElementSibling;
// animating the <ul> elements max-height
if (openFilterPanel.style.maxHeight) {
openFilterPanel.style.maxHeight = null;
} else {
openFilterPanel.style.maxHeight = openFilterPanel.scrollHeight + "px";
}
console.log('openFilterPanel style is changed');
});
}
</script>
<?php } );
I'm pretty new to javascript, I get the basic concepts but this kind of an interference is above my head. I tried refactoring my code, forcing the page to refresh and other such measures. None of these work very well. I also thought I could use a 'loadend' event on the document to re-add my ELs but that didn't work either.
Hoping there is a workaround here, otherwise I might have to find another solution or plugin.
Thanks in advance!
I am integrating a front end html theme with a Laravel app and I am running into an issue with turbolinks not allowing Javascript to append div classes. This is causing the background images to only be displayed on refresh.
<div class="intro-banner" data-background-image="/storage/images/hero.jpg">
<div class="container">
custom.js
/*----------------------------------------------------*/
/* Inline CSS replacement for backgrounds
/*----------------------------------------------------*/
function inlineBG() {
// Common Inline CSS
$(".single-page-header, .intro-banner").each(function() {
var attrImageBG = $(this).attr('data-background-image');
if(attrImageBG !== undefined) {
$(this).append('<div class="background-image-container"></div>');
$('.background-image-container').css('background-image', 'url('+attrImageBG+')');
}
});
} inlineBG();
// Fix for intro banner with label
$(".intro-search-field").each(function() {
var bannerLabel = $(this).children("label").length;
if ( bannerLabel > 0 ){
$(this).addClass("with-label");
}
});
// Photo Boxes
$(".photo-box, .photo-section, .video-container").each(function() {
var photoBox = $(this);
var photoBoxBG = $(this).attr('data-background-image');
if(photoBox !== undefined) {
$(this).css('background-image', 'url('+photoBoxBG+')');
}
});
It looks like this code is only run once: on the initial page load. To get it working for every page load, you will need to run it on turbolinks:load. As the script also appends elements to the page, you need to be careful that you don't end up with unnecessary duplicate elements. Turbolinks stores a copy of the page in its final state before a visitor navigates away. This cached copy will include any appended HTML. So be sure your code checks for the presence of the appended elements before appending, or remove the elements before they are cached.
The following takes the latter approach, by removing elements on turbolinks:before-cache:
/*----------------------------------------------------*/
/* Inline CSS replacement for backgrounds
/*----------------------------------------------------*/
$(document).on('turbolinks:load', function () {
$(".single-page-header, .intro-banner").each(function() {
var attrImageBG = $(this).attr('data-background-image');
if(attrImageBG !== undefined) {
$(this).append('<div class="background-image-container"></div>');
$('.background-image-container').css('background-image', 'url('+attrImageBG+')');
}
});
// Fix for intro banner with label
$(".intro-search-field").addClass(function () {
if ($(this).children("label").length) return "with-label";
});
// Photo Boxes
$(".photo-box, .photo-section, .video-container").css('background-image', function () {
return 'url('+$(this).attr('data-background-image')+')'
})
});
$(document).on('turbolinks:before-cache', function () {
$(".single-page-header, .intro-banner").each(function() {
$(this).children(".background-image-container").remove();
});
});
I have also tidied up some of the jQuery code. Many jQuery functions accept functions as arguments, which simplifies things somewhat, and removes the need to iterate over a jquery selection with each.
Finally, wrapping lots of snippets in $(document).on('turbolinks:load', function () {…} is not great practice as creates a dependency on Turbolinks, and if you ever decided to move to something else, you have to update every place where this is called. If you're feeling adventurous, you may want to create a mini-framework like the one I create here: https://stackoverflow.com/a/44057187/783009
The title of the question expresses what I think is the ultimate question behind my particular case.
My case:
Inside a click handler, I want to make an image visible (a 'loading' animation) right before a busy function starts. Then I want to make it invisible again after the function has completed.
Instead of what I expected I realize that the image never becomes visible. I guess that this is due to the browser waiting for the handler to end, before it can do any redrawing (I am sure there are good performance reasons for that).
The code (also in this fiddle: http://jsfiddle.net/JLmh4/2/)
html:
<img id="kitty" src="http://placekitten.com/50/50" style="display:none">
<div>click to see the cat </div>
js:
$(document).ready(function(){
$('#enlace').click(function(){
var kitty = $('#kitty');
kitty.css('display','block');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec)
{
var endtime= new Date().getTime() + usec;
while (new Date().getTime() < endtime)
;
}
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.css('display','none');
});
});
I have added the alert call right after the sleepStupidly function to show that in that moment of rest, the browser does redraw, but not before. I innocently expected it to redraw right after setting the 'display' to 'block';
For the record, I have also tried appending html tags, or swapping css classes, instead of the image showing and hiding in this code. Same result.
After all my research I think that what I would need is the ability to force the browser to redraw and stop every other thing until then.
Is it possible? Is it possible in a crossbrowser way? Some plugin I wasn't able to find maybe...?
I thought that maybe something like 'jquery css callback' (as in this question: In JQuery, Is it possible to get callback function after setting new css rule?) would do the trick ... but that doesn't exist.
I have also tried to separte the showing, function call and hiding in different handlers for the same event ... but nothing. Also adding a setTimeout to delay the execution of the function (as recommended here: Force DOM refresh in JavaScript).
Thanks and I hope it also helps others.
javier
EDIT (after setting my preferred answer):
Just to further explain why I selected the window.setTimeout strategy.
In my real use case I have realized that in order to give the browser time enough to redraw the page, I had to give it about 1000 milliseconds (much more than the 50 for the fiddle example). This I believe is due to a deeper DOM tree (in fact, unnecessarily deep).
The setTimeout let approach lets you do that.
Use JQuery show and hide callbacks (or other way to display something like fadeIn/fadeOut).
http://jsfiddle.net/JLmh4/3/
$(document).ready(function () {
$('#enlace').click(function () {
var kitty = $('#kitty');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec) {
var endtime = new Date().getTime() + usec;
while (new Date().getTime() < endtime);
}
kitty.show(function () {
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.hide();
});
});
});
Use window.setTimeout() with some short unnoticeable delay to run slow function:
$(document).ready(function() {
$('#enlace').click(function() {
showImage();
window.setTimeout(function() {
sleepStupidly(4000);
alert('now you do see it');
hideImage();
}, 50);
});
});
Live demo
To force redraw, you can use offsetHeight or getComputedStyle().
var foo = window.getComputedStyle(el, null);
or
var bar = el.offsetHeight;
"el" being a DOM element
I do not know if this works in your case (as I have not tested it), but when manipulating CSS with JavaScript/jQuery it is sometimes necessary to force redrawing of a specific element to make changes take effect.
This is done by simply requesting a CSS property.
In your case, I would try putting a kitty.position().left; before the function call prior to messing with setTimeout.
What worked for me is setting the following:
$(element).css('display','none');
After that you can do whatever you want, and eventually you want to do:
$(element).css('display','block');
I am trying to display a table (or ul) that will contain a navigation bar on my page, but only displays the tabs that will contain jquery called divs present on the html.
Essentially, it's a single html document that contains all divs, jquery hides all divs but the first, and the nav bar will allow to navigate through each.
Now I am trying to make it easy to use for my client, so that the menu items will only exist if the div for it also exists. I've got most of it done, the only thing is actually knowing if a div exists.
I tried using this:
if(document.getElementById("page1")) {
document.write("<b>Good morning</b>");}
else
{
document.write("<b>Bad morning </b>");
}
When I place the above code within the div page1, it returns true. Is there no way to do it from the top of the page and not within the div?
Thanks!
Update:
As suggested by many, I have used the following:
$j(document).ready(function(){
//Hide the sections we don't need right away
$j("#page2").hide();
$j("#page3").hide();
$j("#page4").hide();
if ($j('#page1').length > 0) {
var page = 'Excellent Morning' ;
}
});
Then when I try to use:
document.write(page);
It displays the following instead:
[object HTMLBodyElement]
Why not use jQuery since you are already?
if ($('#page1').length > 0) {
// do stuff...
}
EDIT: As davin pointed out, your code should be evaluated after the DOM has been rendered. You can do this by placing it in a $(document).ready call:
$(document.ready(function() {
if ($('#page1').length > 0) {
// do stuff...
}
});
EDIT 2: Based on the OP's edits, a better solution would be to add a placeholder element and to set its content (like FishBasketGordo suggested). An example of this:
$(document.ready(function() {
//Hide the sections we don't need right away
$("#page2, #page3, #page4").hide();
if ($('#page1').length) {
$('#myPlaceHolder').html('<b>Good Morning</b>');
}
else
{
$('#myPlaceHolder').html('<b>Bad Morning</b>');
}
});
Somewhere else in the document...
<span id="myPlaceHolder"></span>
If you place it at the top of the page, the page1 div doesn't exist when the code runs. If you are using jQuery, place the code in a $(document).ready event. Then, you can put it where you want it within the markup. Here's an example:
$(document).ready(function() {
if (document.getElementById("page1")) {
document.write("<b>Good morning</b>");
} else {
document.write("<b>Bad morning </b>");
}
});
Although, rather than doing a document.write, I would consider having a placeholder span or div, and setting it's innerHTML property (or use jQuery's html method). I would also use CSS for my style instead of <b> tags, but that's another matter entirely.
You can use
if ($(selector).length > 0) {
// element exists
}
or you can check out this post for a more elegant solution
Is there an "exists" function for jQuery?
I am currently using jQuery-Smooth-Scroll to smoothly scroll up and down to various anchor positions on one of my pages (Page 1). However, what I would also like to be able to do is, from another page (Page 2), link to Page1 (appending #bookmark to the url) and have jQuery-Smooth-Scroll pick up on the fact I am calling the page with a #bookmark and have it smoothly scroll down to the relevant position once the page has completed loading. I don't know if this is a possibility or not?
This is the version of Smooth-Scroll that I'm using:
https://github.com/kswedberg/jquery-smooth-scroll
I'm still relatively new to jQuery so I may be overlooking something obvious.
Ajma's answer should be sufficient, but for completeness:
alert(location.hash)
Edit: a more complete example:
// on document.ready {
if (location.hash != '') {
var a = $("a[name=" + location.hash.substring(1) + "]");
// note that according to w3c specs, the url hash can also refer to the id
// of an element. if so, the above statement becomes
// var a = $(location.hash);
if (a.length) {
$('html,body').animate({
scrollTop: $(a).offset().top
}, 'slow');
}
}
// }
It's possible, you want to put a call into the smooth scroll function when the page is finished loading. in jQuery, it's using $(document).ready(function () { your code } );
You'll need to put something in to parse your url to extract the #bookmark and then call the smooth scroll.