Custom title for Magnific Popup - javascript

I'm trying to get Magnific Popup to display a title based on other elements around the target it uses. Given the following markup, I want the title to be "Foobar".
<div class="container">
<article>
<h2>Foo</h2>
<figure>
<a href="img/img-1.jpg">
<img src="img/img-1.jpg" />
</a>
<figcaption>
bar1
</figcaption>
</figure>
</article>
<article>
<h2>Foo</h2>
<figure>
<a href="img/img-2.jpg">
<img src="img/img-2.jpg" />
</a>
<figcaption>
bar2
</figcaption>
</figure>
</article>
</div>
What I've tried while looking for solutions online (including this one on StackOverflow) is the following code:
$('.container').magnificPopup({
delegate: 'article figure a',
type: 'image',
titleSrc: function(item) {
return item.el.parent('article').find('h2').text() + item.el.parent('article').find('figcaption').text();
},
gallery:{enabled:true}
});
Figuring the function might have been an issue I've even tried to simply return a constant string, but that seemed to do nothing:
titleSrc: function(item) {
return "fake Foobar";
}
Does anyone have any clues as what I'm doing wrong?
NOTE: It does work if I use titleSrc: 'title', but that's not the behavior I want, as it makes me have to duplicate content in the markup.

As per documentation titleSrc:{} should be inside image:{} and you can use item.el.parents() instead of item.el.parent().
Corrected Code
$('.container').magnificPopup({
delegate: 'article figure a',
type: 'image',
gallery:{enabled:true},
image: {
titleSrc: function(item) {
return item.el.parents('article').find('h2').html() + item.el.parents('article').find('figcaption').html();
}
}
});

My designed required me to have a title & description with each Image slide hence I required a custom title in magnific popup, I tried the answer from #krizna but I was only getting the title, to debug I went into js file of the magnefic popup (jquery.magnefic-popup.js) and found the function which gets called when custom Markup is parsed it is called appropriately "_getTitle".It get an item object as parameter.I logged this item object to find it had data attribute in which our item parameter goes.
I initialised the popup using items option (3rd way to initialise in the docs) here is my custom item object
items: [
{
src: 'https://c6.staticflickr.com/9/8785/16698139093_3c30729f1b.jpg"',
title: 'We buy Nike shoes',
description:'You might ask, why PhotoSwipe doesn\'t add this code automatically via JS, reason is simple – just to save file size, in case if you need some modification of layout'
},
{
src: 'https://c2.staticflickr.com/8/7662/17307905305_92ae5081bf_b.jpg"',
title: 'You can add HTML code dynamically via JS (directly before the initialization), or have it in the page initially (like it\'s done on the demo page)',
description:'You might ask, why PhotoSwipe doesn\'t add this code automatically via JS, reason is simple – just to save file size, in case if you need some modification of layout'
},
{
src: 'https://farm2.staticflickr.com/1043/5186867718_06b2e9e551_b.jpg',
title: 'Avoid serving big images (larger than 2000x1500px) for mobile, as they will dramatically reduce animation performanc',
description:'You might ask, why PhotoSwipe doesn\'t add this code automatically via JS, reason is simple – just to save file size, in case if you need some modification of layout'
}
],
each item has src of image, a title & a description , now my titleSrc function is
titleSrc: function(item) {
return '<p id="gallery-image-title">' + item.data.title +'</p>' + '<p id="gallery-image-description">' + item.data.description +'</p>';
}
I also modified the "_getTitle" function because it only parsed title property in item object(commented the first two lines), my "_getTitle" now looks like this
_getTitle = function(item) {
console.log(item);
/*if(item.data && item.data.title !== undefined)
return item.data.title;*/
var src = mfp.st.image.titleSrc;
if(src) {
if($.isFunction(src)) {
return src.call(mfp, item);
} else if(item.el) {
return item.el.attr(src) || '';
}
}
return '';
};
Now I have control over markup & have 2 dynamic src for title property.

Related

Iterate through an array on click and change albums via another click

I have a ngFor loop which iterates over an array of famous people (see below), this is fine but I would now like to add in image gallery which is where things get tricky. Each person will have 2 photo albums, the albums will be simply strings of urls to remote photos - the first problem is that I would like to be able to click the image tag (or element close by) and on each click I want the image src to change to the next image, when it reaches the end ideally I would like it to go back again to the start or just stop.
this is tricky enough - but each person will have a secondary photo album which is much the same as the first i.e Array however - it gets activated when the user clicks the changeAlbum() which is on a thumbnail image below - the idea is that the first image of the array not in use (as clicking changeAlbum() makes it inactive ) populates this thumbnail and the gallery then uses the images from the secondary gallery - so essentially its like a complex toggle() between the 2 galleries combined with a click to iterate over the array of each gallery - it might even be cool if when you iterate through the first gallery that it auto switches to the secondary one and vice versa
Im not entirely sure how to approach it though - the html and the json struct are the only solid things I have in place.
<div class="card" *ngFor="let c of item" >
<div class="card-content">
<div class="card-image">
<img (click)="changePhoto()" [src]="{{ output image here from the click }}" />
</div>
<div class="card-titles">
<h1>
{{ c.name }}
</h1>
</div>
<div class="card-image">
<img (click)="changeAlbum()" [src]="{{ show image from album not in use here}}" />
</div>
</div>
</div>
The data structure I have so far is like this
item = [{
id: 1,
name: "Joe Jimbob",
album_Main: [{
selected: true,
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
}],
album_Secondary: [{
selected: false,
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
}]
}]
This is also relatively solid but im very open to any help anyone can give
changeAlbum(item:any) :void {
if( item.album_Main.selected ) {
item.album_Main.selected == false
} else {
item.album_Secondary.selected == false
}
}
and the change...
changePhoto(photos: Array<string>) :void {
// not sure how to interate this one by one on click
console.log( photos.length )
}
Check this Stackblitz example I made based on your code: https://stackblitz.com/edit/angular-pk6cu8
The important changes are these in app.component.ts
photoIndex = 0;
personIndex = 0;
selectedAlbum = this.item[0].album_Main;
altAlbum = this.item[0].album_Secondary;
isMainAlbumActive = true;
changePhoto() {
if (this.photoIndex < this.selectedAlbum.length - 1) {
this.photoIndex++;
} else {
this.photoIndex = 0;
}
}
changeAlbum() {
if (this.isMainAlbumActive) {
this.selectedAlbum = this.item[this.personIndex].album_Secondary;
this.altAlbum = this.item[this.personIndex].album_Main;
} else {
this.selectedAlbum = this.item[this.personIndex].album_Main;
this.altAlbum = this.item[this.personIndex].album_Secondary;
}
this.isMainAlbumActive = !this.isMainAlbumActive;
}

lazy loading images on scroll and "come into view"

I am using Lazy as a lazy image loading plugin. I have a div where I load divs like this:
<div class="nano-content" id="lScroll">
/*... MORE LIKE THIS ... */
<div class="card">
<div class="city-selected city-medium clickChampion pointer"
data-champ-id="1">
<article>
<div class="info">
<div class="city">
CHAMPNAME
</div>
</div>
</article>
<figure class="cFigure lazy" data-src="images/champions/CHAMPNAME_0.png"></figure>
</div>
</div>
/*... MORE LIKE THIS ... */
</div>
So I initiate the plugin and it works for the first ones visible and when I scroll:
var $lazy = $('#lScroll .lazy');
if ($lazy.length) {
$lazy.Lazy({
appendScroll: $('#lScroll')
});
}
But now I have a function that "filters" the divs by their attributes when I enter sth in my search input and it fails to load the image when the according div is shown:
$(document).on("keyup", "#searchVod", function () {
var $search = $(this);
var $sVal = $search.val().toLowerCase();
if ($sVal !== "") {
$(".vodCard").hide();
$('[data-champ*="' + $sVal + '"]').show();
$('[data-role*="' + $sVal + '"]').show();
} else {
$(".vodCard").show();
}
});
I tried bind: "event" /w and w/out delay: 0 (loading the plugin in the search function) but when I searched it would load ALL images immediately in the background.
Any hint highly appreciated
UPDATE: I just noticed in Chrome DevTab after entering one letter in my searchbox it loads ALL the images and eventually the one I am searching for (if its the last it takes some time (30MB sth)
There is an excellent library called Lozad.js which can help you to make it easier to load your images like lazy load do but in easier way.
You can download it here from Github.
Demo page here.
Explanation:
This library will load your images one by one on scrolling to each image anchor by class name.
Example
HTML:
At the header ->
<script src="https://cdn.jsdelivr.net/npm/lozad"></script>
Image element should looks like this:
<img class="lozad" data-src="image.png">
Javascript
// Initialize library
lozad('.lozad', {
load: function(el) {
el.src = el.dataset.src;
el.onload = function() {
el.classList.add('fade')
}
}
}).observe()
Hope it will help you.
Best,
Ido.

Loading WordPress Posts on a html page

I am trying to load posts outside WordPress on a static html page. So far I have a working example using React, http://v2.wp-api.org/ and https://github.com/mzabriskie/axios. This working example using react currently displays the posts properly but it is fragile and there is no fall back. Codepen example here, https://codepen.io/krogsgard/pen/NRBqPp/
I use this example to get my feed source using axios axios.get(this.props.source). Then I use the examples react function to grab my latest three posts, including titles and images and load them into a static html page via
render: function render() {
return React.createElement(
"div",
{ className: "post-wrapper" },
this.state.posts.map(function (post) {
return React.createElement(
"div",
{ key: post.link, className: "post" },
React.createElement(
"h2",
{ className: "post-title" },
React.createElement("a", {
href: post.link,
dangerouslySetInnerHTML: { __html: post.title.rendered }
})
),
post.featured_media ? React.createElement(
"a",
{ href: post.link },
React.createElement("img", { src: post._embedded['wp:featuredmedia'][0].source_url })
) : null
);
})
);
}
My blog's source wp json is
React.render(React.createElement(App, { source:
"myBlogURL.com/wp-json/wp/v2/posts/?_embed&per_page=3" }),
document.querySelector("#blog-post"));
Which correctly loads my latest 3 blog posts into the <div id="blog-posts">
I'm looking for a vanilla js way to do this with some fallback helpers. In case I forget to include the featured image to a post, the posts will not fail to load. Any ideas or examples would greatly be appreciated!
You are working way to hard for this. Wordpress CMS is designed for stuff like this. You are able to display posts in the form of a RSS feed by categories, tags and other taxonomies. pretty easily
• If you are not too good with code, you can find many widgets that'll take care of most of the work.
• If you need to do it yourself, below should get you there with JSON / jQuery.
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js'></script>
<script type="text/javascript">
var MYBLOG_LIMIT = 3;
var MYWRAPPER_CLASS = 'homeblog';
var WP={open:function(b){var a={posts:function(){var d=MYBLOG_LIMIT;var e=0;var c={all:function(g){var f=b+"/api/get_recent_posts/";f+="?count="+d+"&page="+e+"&callback=?";jQuery.getJSON(f,function(l){var k=l.posts;for(var j=0;j<k.length;j++){var h=k[j];h.createComment=function(i,m){i.postId=h.id;a.comments().create(i,m)}}g(k)})},findBySlug:function(f,h){var g=b+"/api/get_post/";g+="?slug="+f+"&callback=?";jQuery.getJSON(g,function(i){h(i.post)})},limit:function(f){d=f;return c},page:function(f){e=f;return c}};return c},pages:function(){var c={findBySlug:function(d,f){var e=b+"/api/get_page/";e+="?slug="+d+"&callback=?";jQuery.getJSON(e,function(g){f(g.page)})}};return c},categories:function(){var c={all:function(e){var d=b+"/api/get_category_index/";d+="?callback=?";jQuery.getJSON(d,function(f){e(f.categories)})}};return c},tags:function(){var c={all:function(e){var d=b+"/api/get_tag_index/";d+="?callback=?";jQuery.getJSON(d,function(f){e(f.tags)})}};return c},comments:function(){var c={create:function(f,e){var d=b+"/api/submit_comment/";d+="?post_id="+f.postId+"&name="+f.name+"&email="+f.email+"&content="+f.content+"&callback=?";jQuery.getJSON(d,function(g){e(g)})}};return c}};return a}};
var blog = WP.open('https://www.fldtrace.com');
blog.posts().all(function(posts){
for(var i = 0; i < posts.length; i++){
jQuery('.'+MYWRAPPER_CLASS).append(function(){
return (posts[i].thumbnail) ? '<a class="lastpost_title" href="'+posts[i].url+'">
<h4>'+posts[i].title+'</h4>
</a><img src="'+posts[i].thumbnail+'"/>' : '<a href="'+posts[i].url+'">
<h4>'+posts[i].title+'</h4>
</a>';
});
}
});
</script>
<div class="homeblog">
</div>
You need to configure the next options
var MYBLOG_LIMIT = 1; will define how many posts will show. By default
is 1. var MYWRAPPER_CLASS = ‘homeblog’; – the class name of HTML
element where the post(s) will be shown. var blog =
WP.open(‘https://www.fldtrace.com/’); – this should link to your blog
main domain (mandatory) by default, will be shown the post thumbnail
and title both linked. The rest is CSS customization, including
adjusting the thumbnail size.
Read more here at source article.

Activate Current Content Part in jQuery UI Accordion Menu

I'm trying to figure out how to dynamically activate the correct content part of a jQuery UI Accordion menu depending on the page currently being viewed. I've searched extensively and it seems like others have had issues with this in the past, but I haven't yet found a solution that works for me. I know that active can be set to open a certain index of the menu, but I need to do this dynamically.
I'm thinking that I can achieve what I want using the activate method, I just can't seem to figure it out. I'd like to stay away from setting cookies as that usually won't work with back/forward buttons and direct navigation via a specific url. Anyone have any ideas? Thanks in advance!
Here is the simplified structure of my menu:
<div id="menu">
<div id="sections">
<div class="grouping accordion">
<a id="heading1" href="#">Heading #1</a>
<div class="sub-items">
Item #1
<br />
Item #2
</div>
</div>
<div class="grouping accordion">
<a id="heading2" href="#">Heading #2</a>
<div class="sub-items">
Item #4
<br />
Item #6
</div>
</div>
</div>
</div>
And here is my jQuery Accordion init:
$('#sections').accordion({
header: '> .accordion > a',
autoHeight: false,
collapsible: true,
active: false,
animated: 'slide'
});
So if you are currently on the /item4 page for example, the group under Heading #2 should be expanded.
EDIT:
I found what seems to be a pretty good solution and posted that as an answer below, hopefully this will help someone with a similar problem!
To activate a specific tab, you'll want to use the accordion('activate', index) method. Example:
$( "#sections" ).accordion('activate', 2);
However, you will need something that defines an index key per page. You can probably even generate this dynamically. I would probably create a Pages object:
Pages = {
"heading1" : {
"id": 1,
"is_active": false,
"items": {
"item1": {
"href": "item1",
"name": "Item #1"
},
"item2": {
"href": "item2",
"name": "Item #2"
}
}
},
"heading2" : {
/* etc*/
},
}
With some hackish jQuery magic, you can loop through your headings
var active_heading_index = null;
$.each(Pages, function(heading) {
$.each(heading.items, function(item) {
if($(location).attr('href') == item.href) {
heading.is_active = true;
// or
active_heading_index = heading.id
}
});
});
if(active_heading_index) $( "#sections" ).accordion('activate', active_heading_index);
Anyhow, I'm sure there are cleaner and more efficient ways of doing it.
While working on some CSS for the active headings on the menu I stumbled on a pretty clean and easy solution. Looks like I might have been overthinking things!
Using the same HTML as in the question, here's the JavaScript that is working for me:
//accordion menu
$('#sections').accordion({
header: '> .accordion > a',
autoHeight: false,
collapsible: true,
active: '.selected',
animated: 'slide'
});
//add currentPage class to current menu item based on url
var url = window.location.href;
url = url.substr(url.lastIndexOf("/") + 1);
$("#sections").find("a[href='" + url + "']").addClass("currentPage");
//get id of header anchor tag
var headerId = $(".currentPage").parents(".accordion").children("a").attr("id");
//check if headerId is set, if so activate that id
if (headerId) {
$('#sections').accordion('option', 'animated', false);
$('#sections').accordion('activate', $('#' + headerId));
$('#sections').accordion('option', 'animated', 'slide');
}
This solution is pretty simple, it gets the current page from the url and compares it against each link in the accordion menu. If it finds a match, it gives that link a class of currentPage (which allows us to then style that link accordingly via css). Then it looks for a parent of that link with a class of .accordion, finds the first child link (the accordion header) and grabs the header's id. Assuming a header id has been found, we can then use the activate method to expand the correct section.
If you are going back to the server for every page click (standard non Ajaxy way), the server can add a "selected" class to the proper node. So you'd get back something like this at the client (I'm only writing the essential code, skipping most of the labels).
<ul id="menu">
<li>
<ul>
<li>
</li>
<li class="selected">
Menu 102
</li>
<ul>
</li>
<li>
...
</li>
<ul>
Then simply find the proper index to give to the activate property of the accordion.
$(document).ready(function() {
var index = $("li.selected").parents("li").last().index();
$("#menu").accordion({
active: index,
collapsible: true
});
});
The parents("li").last() jQuery returns the top most element. This only works if you only have one sub element with the class "selected", which should be the case for your menu.
I did it using this code:
var newsNum = parseInt(window.location.hash.slice(1));
$(document).ready(function(){
$("#menu").accordion('activate', newsNum );
});
And the url looks like example.com/index.html#1

Handle HTML code block as an object?

I have a div that basically represents a book (so a nice div layout with an image of the book, title, price, red background if on sale etc.). So what i do is to get the properties of a book from the database, insert the values in kind of an html template and display it.
Now, once it is displayed i hate how i have to handle the data. I have to either parse css properties to figure out if a book is on sale for an example or i have to keep the data in another place as well (some javascript array or use the jquery data feature). The first option is ugly the second one would require me to update two things when one property changes - which is ugly as well.
So what i would like is to handle that block of html (that represents a single book) as an object. Where i can call obj.setPrice(30); and things like that and finally call obj.update(); so it would update its appearance.
Is there anyway to accomplish this ? Or something like this ? I just feel that once i render the data as html i loose control over it :(
Suppose your html div is like this
<div id="book1">
<div id="price">$30</div>
...
</div>
You can define a Book object as follows:
var Book = function(name) {
this.name = name;
}
Book.prototype = {
setPrice : function(price) {
this.price = price;
},
update : function() {
pricediv = document.getElementById(this.name)
pricediv.innerHTML = '$'+price;
}
}
var book = new Book('book1')
book.setPrice(50);
book.update();
I guess your best shot is write your own object / methods for that.
var Book = function(){
var price = args.price || 0,
color = args.color || 'red',
height = args.height || '200px',
width = args.width || '600px',
template = "<div style='background-color: COLOR; width=WIDTH; height: HEIGHT;'><span>$PRICE</span><br/></div>";
return {
setPrice: function(np){
price = np;
return this;
},
setColor: function(nc){
color = nc;
return this;
},
setHeight: function(nh){
height = nh;
return this;
},
render: function(){
template = template.replace(/COLOR/, color);
template = template.replace(/PRICE/, price);
// etc
// use jQuery or native javascript to form and append your html
$(template).appendTo(document.body);
}
};
};
This is just a pretty basic example which can be optimized like a lot. You may even think about using John Resigs microtemplate (http://ejohn.org/blog/javascript-micro-templating/)
Usage from the above example would look like:
var book = Book({
price: 30,
color: 'blue'
});
book.render();
To change values:
book.setPrice(140).setColor('yellow').setHeight('500').render();
I have been playing around with Microsoft's proposal for jQuery Templates and Data Linking and so far it's going awesome.
TLDR, checkout this demo.
It's extremely easy to just link up a piece of HTML with a JavaScript object and from thereon, only update the JavaScript object and the HTML updates automatically.
Here's a simple example. Create the HTML that will represent your widget.
<div class="book">
<img width="100" height="100" src="" />
<div class="title"></div>
<div class="price"></div>
</div>
Then create a JavaScript object and dynamically link it to the HTML above. Here is a sample object:
var book = {
title: "Alice in Wonderland",
price: 24.99,
onSale: true,
image: "http://bit.ly/cavCXS"
};
Now onto the actual linking part. The items we are going to link up are:
A data-onsale attribute in the outer div which will be either "true" or "false"
The image src attribute to the image property of our book
title div to the title property
price div to the price property
The following sets up the linking. Note that we are only doing a one way linking here, but it's possible to setup a two way linking also using the linkBoth function.
$(book)
.linkTo('title', '.title', 'html')
.linkTo('price', '.price', 'html')
.linkTo('image', '.book img', 'src')
.linkTo('onSale', '.book', 'data-onsale')
That's it. From now onwards, just update the book object and the HTML will automatically update. Update the properties of the book like you would update html attributes using the attr function.
$(book).attr({
price: 14.75
});
or
$(book).attr('price', 14.75);
The code above is only using Data Linking, but the proposal also mentions combining data linking with templates which would make this even more easier. From what I reckon, you would be able to do this and get the above functionality:
<script id="bookTemplate" type="text/html">
<div class="book" data-onsale="{{link onSale }}">
<img width="100" height="100" src="{{link image }}" />
<div class="title">{{link title }}</div>
<div class="price">{{link price }}</div>
</div>
</script>
Link the above template with the book object in one step and add it to the page:
$('#bookTemplate').render(book).appendTo('body')
Update the properties of the book object, and changes will reflect.

Categories

Resources