Masonry and imagesLoaded error - jquery-masonry

I combined masonry with imagesLoaded like this:
var container = document.querySelector('.masonry-container');
var msnry;
// initialize Masonry after all images have loaded
imagesLoaded( container, function() {
var msnry = new Masonry( container, {
itemSelector: '.masonry-item'
}).resize();
});
But getting error: Uncaught TypeError: Cannot read property 'length' of null
What am I doing wrong.
Edit
I'm having two masonry calls, maybe that causes problem, another one is the same, one after another:
var container = document.querySelector('.gallery');
var msnry;
// initialize Masonry after all images have loaded
imagesLoaded( container, function() {
var msnry = new Masonry( container, {
itemSelector: '.gallery-item'
}).resize();
});

You have multiple errors in your jsfiddle which I assume are in your page as well. First, id's are unique, you can't have multiple id's of the same name (id="galery-item"). Your calling masonry on ".gallery" yet you only have the id="gallery", no class="gallery", and finally you set your itemSelector: '.gallery-item', yet even if you had the figures set as a class, you have it called galery-item.
Here is a working jsfiddle with classes and id's set correctly.
js:
var container = document.querySelector('.gallery');
var msnry;
// initialize Masonry after all images have loaded
imagesLoaded( container, function() {
var msnry = new Masonry( container, {
itemSelector: '.galery-item'
});
});
working html:
<div id="gallery" class="gallery">
<figure class="galery-item">
<figcaption>
<img src="http://placehold.it/150x150">
</figcaption>
</figure>
<figure class="galery-item">
<figcaption>
<img src="http://placehold.it/150x150">
</figcaption>
</figure>
<figure class="galery-item">
<figcaption>
<img src="http://placehold.it/150x150">
</figcaption>
</figure>
<figure class="galery-item">
<figcaption>
<img src="http://placehold.it/150x150">
</figcaption>
</figure>
<figure class="galery-item">
<figcaption>
<img src="http://placehold.it/150x150">
</figcaption>
</figure>
</div>

Found a problem. In masonry call I had var container twice which just overwrites one another. Ex. in specific page I just had div with class .gallery but not .masonry-container, and var container is overwritten with .masonry-container which doesn't exists, so it returns null. Solution is:
var $container = $('.masonry-container');
$container.imagesLoaded(function(){
$container.masonry({
itemSelector : '.masonry-item'
});
});
var $container2 = $('.gallery');
$container2.imagesLoaded(function(){
$container2.masonry({
itemSelector : '.gallery-item'
});
});
As seen here: Using jQuery Masonry Many times in the same site

Related

How to scrape from 2 divs that are on the same level with Cheerio

I'm trying to web scrape content from 2 different divs that are on the same level. I'm using NodeJS, Axios, Cheerio and Express.
Basically, I'm trying to collect an image and the info related to it, but they are placed of different divs that are on the same level. Using the "main" doesn't seem to work in my case.
<div class="main">
<div class="one">
// image
</div>
<div class="two">
// info
</div>
</div>
Below is my code to get the data from a website:
var leafletList = $('.store-flyer__info', html).each(function() {
let leaflet = {
title: $(this).find('h3').text(),
image: $(this).find('source').attr('srcset'),
link: $(this).find('a').attr('href'),
validDate: $(this).find('small').text().slice(3,-1)
}
leaflets.push(leaflet)
})
Below is the website's HTML:
The way my code is right now, it's obviously getting only the title, link and validDate. But anyone knows how can I get the the srcset from the other div? I've also tried the following method, but it doesn't work:
var leafletList = $('.store-flyers', html).each(function() {
let leaflet = {
title: $(this).find('.store-flyer__info h3').text(),
image: $(this).find('.store-flyer__front source').attr('srcset'),
link: $(this).find('.store-flyer__info a').attr('href'),
validDate: $(this).find('.store-flyer__info small').text().slice(3,-1)
}
leaflets.push(leaflet)
})
There are many ways to get the result based on the HTML snippet you show, with the caveat that the developer tools can be misleading. It shows elements created after page load with JS, which you won't have if you're only requesting the raw page HTML.
With that in mind, here are a few options:
const cheerio = require("cheerio"); // ^1.0.0-rc.12
const html = `
<div class="store-flyer">
<picture>
<source srcset="foo.jpeg" type="image/webp">
<source srcset="bar.jpeg" type="image/jpeg">
</picture>
</div>
<div class="store-flyer">
<picture>
<source srcset="quux.jpeg" type="image/webp">
<source srcset="garply.jpeg" type="image/jpeg">
</picture>
</div>
`;
const $ = cheerio.load(html);
const result = [...$(".store-flyer")].map(e => ({
// select using `.first()` and `.last()` Cheerio methods:
firstImage: $(e).find("source").first().attr("srcset"),
secondImage: $(e).find("source").last().attr("srcset"),
// select using CSS attribute selectors:
firstImageByType: $(e).find('source[type="image/webp"]').attr("srcset"),
secondImageByType: $(e).find('source[type="image/jpeg"]').attr("srcset"),
// select as an array of all <source> elements:
allImages: [...$(e).find("source")].map(e => $(e).attr("srcset")),
}));
console.log(result);
Output:
[
{
firstImage: 'foo.jpeg',
secondImage: 'bar.jpeg',
firstImageByType: 'foo.jpeg',
secondImageByType: 'bar.jpeg',
allImages: [ 'foo.jpeg', 'bar.jpeg' ]
},
{
firstImage: 'quux.jpeg',
secondImage: 'garply.jpeg',
firstImageByType: 'quux.jpeg',
secondImageByType: 'garply.jpeg',
allImages: [ 'quux.jpeg', 'garply.jpeg' ]
}
]
Prepending .store-flyer__front to your source selectors might be a good idea if you need to disambiguate.
With cheerio, you can access node properties such as:
parentNode
previousSibling
nextSibling
nodeValue
firstChild
childNodes
lastChild
<div class="main">
<div class="one">
// image
</div>
<div class="two">
// info
</div>
</div>
.main.firstChild is .one
.one.nextSibling is .two
.main.lastChild is .two
.two.previousSibling is .one

CKEditor5 image resize not working correctly

I integrate CKEditor5 editor on my project to do email template. Below is my CKEditor code:
<CKEditor
editor={ ClassicEditor }
config={ {
toolbar: [ 'heading', 'bold', 'italic', 'bulletedList', 'numberedList', 'blockQuote' , 'fontColor' , 'fontBackgroundColor' , 'code', 'uploadImage'],
ckfinder:{
uploadUrl:'upload url'
}} }
data={template}
onReady={ editor => {
// You can store the "editor" and use when it is needed.
//console.log( 'Editor is ready to use!', editor );
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
console.log(data)
setTemplate(data)
} }
/>
When I store image using CKEditor in my database the CKEditor code comes like this:
<figure class="image image_resized" style="width:2.82%;"><img src="https://www.w3schools.com/html/pic_trulli.jpg"></figure><p> welcome to {{first_name}}</p>
My template comes like this:
I styled in CKEditor like the below image, but the image resize not coming in my template:
The issue is when use figure tag like this it works:
<figure>
<img src="pic_trulli.jpg" alt="Trulli" style="width:10%">
<figcaption>Fig.1 - Trulli, Puglia, Italy.</figcaption>
</figure>
but in CKEditor automatically code generating like this:
<figure style="width:20%;">
<img src="https://www.w3schools.com/html/pic_trulli.jpg">
<figcaption>Fig.1 - Trulli, Puglia, Italy.</figcaption>
</figure>
look at this, I guess you need to change the figure tag display to block.
<html>
<head>
<style>
figure {
display: block;
}
</style>
</head>
<body>
<p>A figure element is displayed like this:</p>
<figure>
<img src="img_pulpit.jpg" alt="The Pulpit Rock" width="2%" >
</figure>
<p>Change the default CSS settings to see the effect.</p>
</body>
</html>

How to apply masonry to items appended by ajax call

I have a picture grid and in the Mobile View (320 X 480), there is a "Load More" button. The container div is as follows:
<div id="divMoments" class="grid" data-masonry='{ "itemSelector": ".grid-item"}'>
<div class="grid-item">
<div class="gridContainer">
<img src="ImageURL" />
<p>OwnerName</p>
</div>
</div>
</div>
On the button click, it triggers an ajax call. The received result is a html string of many such grid items:
"<div class=\"grid-item\">imagex<div>
<div class=\"grid-item\">imagey<div>
..."
After appending the string to the container, I have the jQuery code to reload masonry, but all the images are overlapped. When I check the html, the masonry css is applied to all the items.
function GetNextSet() {
jQuery.ajax({
url: "/api/sitecore/Moment/GetNextSet",
type: "POST",
context: this,
success: function (data) {
ShowNextResultSet(data);
}
});
}
function ShowNextResultSet(data) {
var $content = jQuery(data.ResultSet);
jQuery("#divMoments").append($content).masonry('appended', $content);
jQuery("#divMoments").masonry('reloadItems');
jQuery("#divMoments").masonry();
}
using masonry v4.1.1
Re-applying masonry after a delay worked for me.
function ShowNextResultSet(data) {
var $content = jQuery(data.ResultSet);
jQuery("#divMoments").append($content).masonry('appended', $content);
setTimeout(function () {
jQuery("#divMoments").masonry('reloadItems');
jQuery("#divMoments").masonry();
}, 100);
}

Cannot use $(this) in $.getJSON in .each

Im building a custom Minecraft Server Status and hit a problem. The first version of this was successful but the code was rather long and I decided to make it better and shorter. The script is supposed to fill the elements of each .server but it doesn't work.
<div class="server_status">
<div class="container servers_info">
<h1>My Network</h1>
<div id="of the server" class="server" title="of the server" server-ip="0.0.0.0">
<div class="name"></div>
<div class="count"><i class="fa fa-spinner fa-spin"></i></div>
<div class="players">Loading player data <i class="fa fa-spinner fa-spin"></i></div>
<div class="status"></div>
</div>
<div id="of the server" class="server" title="of the server" server-ip="0.0.0.0">
<div class="name"></div>
<div class="count"><i class="fa fa-spinner fa-spin"></i></div>
<div class="players">Loading player data <i class="fa fa-spinner fa-spin"></i></div>
<div class="status"></div>
</div>
<!-- ..... more servers -->
<span class="total"><i class="fa fa-spinner fa-spin"></i></span>
</div>
$(document).ready(function ping() {
$( ".servers_info .server" ).each( function() {
var name = $(this).attr( "title" );
var ip = $(this).attr( "server-ip" );
var id = $(this).attr( "id" );
var total = 0;
var call = "Get Avatar List adress";
//Set the name:
$(".name",this).html(name);
//Gets the data:
$.getJSON("http://mcapi.ca/v2/query/info/?ip=" + ip, function (json) {
//Checks The status and applies visual effects:
if (json.status !== "false") {
$(".status",this).html("<span class=\"l-online\">" + json.ping + " ms</span>");
$(this).removeClass('blur');
} else {
$(".status",this).html("<span class=\"l-offline\">0 ms</span>");
$(this).addClass('blur');
};
});
});
//Sets Refresh rate of 10s
setTimeout(ping, 10000);
});
I narrowed down the problem to the $.getJSON part. The data is retrieved correctly but cannot be placed in its respective DIVs. The only difference with the first version of the script is that I used 4 getJSON separately for each of the servers I wanted to display. Now using .each to combine it for all 4 of them and also $(this) to use relative objects.
I suspect the problem is in th usage of $(this) in .get but I'm nnot sure and don't know how to fix it.
As you suspect, the issue is the $(this). part. Inside the $.getJSON callback this no longer refers to the DOM object that triggered the event.
To fix this you can either:
Add a .bind(this) to the callback function. No changes required inside the function itself.
$.getJSON(url, function(json) {
/* all your code here */
}.bind(this)
);
Or save the reference to this before $.getJSON and use it inside the callback.
var _this = this;
$.getJSON(url, function(json) {
/* replace all references of this to _this for example*/
$(_this).removeClass('blur');
});
Hope that helps

Overlapping in Masonry onload

I'm having the classic overflow problem with masonry. I'm trying to load twitter cards but onload, they are still overlapping each other. They work once the screen is resized. FYI, I have imageLoad and masonry in there for sure. I'm doing this in rails so not sure how I would make a jsfiddle. Any help is much appreciated. Thanks!
here is my js code:
$(document).ready(function() {
$('.container').imagesLoaded( function(){
$('.container').masonry({
columnWidth: '.tweet-box',
itemSelector: '.tweet-box'
});
});
});
index.html.erb:
<main class="container">
<% #tweet.search("cnn").take(9).each do |j| %>
<section class="tweet-box">
<p>
<blockquote class="twitter-tweet"><p></blockquote>
<script src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</p>
</section>
<% end %>
</main>
I ended up fixing my problem with some recursion. FYI, I don't know if this is the best fix but it got it working for me. Open to a better answer if someone has anything.
$(document).ready(function() {
check_size();
check_width();
});
function check_size()
{
if($('.tweet-box').first().height() == 0)
{
// alert('loop called: ' + $('.tweet-box').first().height())
setTimeout('check_size()', 20);
}
else
{
// alert('initialized!!!! boo-ya')
var $container = $('.container').imagesLoaded( function() {
$container.isotope({
// options
itemSelector: '.tweet-box',
layoutMode: 'masonry'
});
});
}
}
This Worked for me, with the imagesloaded script installed.
<script src="/js/masonry.pkgd.min.js"></script>
<script src="/js/imagesloaded.pkgd.min.js"></script>
<script>
docReady(function() {
var grid = document.querySelector('.grid');
var msnry;
imagesLoaded( grid, function() {
// init Isotope after all images have loaded
msnry = new Masonry( grid, {
itemSelector: '.grid-item',
columnWidth: '.grid-sizer',
percentPosition: true
});
});
});
</script>
For me a combination of the small script "imagesloaded" and a few additional lines of jQuery that fire masonry after all images are loaded did the trick. My page footer now looks like this:
<script src="/js/masonry.pkgd.min.js"></script>
<script src="/js/imagesloaded.pkgd.min.js"></script>
<script>
$('#catalogue').imagesLoaded( function(){
$('#catalogue').masonry({
columnWidth: 10,
itemSelector: '.item',
isAnimated: !Modernizr.csstransitions
/* isFitWidth: true */
});
});
</script>
I don't remember where I got the pinter from, but the script imagesloaded can be found here: https://github.com/desandro/imagesloaded

Resources