I am trying to create a product builder and I have found a template on codyhouse that looks like it will do the job. The only issue is that the button to select just stays on loading and doesn't progress.
This is the link to the website. https://codyhouse.co/demo/product-builder/index.html
Once the radio button is selected, it keeps loading. Any help would be greatly appreciated as I am out of my depth. I suspect this is an issue with JS but I am not sure.
jQuery(document).ready(function($){
function ProductBuilder( element ) {
this.element = element;
this.stepsWrapper = this.element.children('.cd-builder-steps');
this.steps = this.element.find('.builder-step');
//store some specific bulider steps
this.models = this.element.find('[data-selection="models"]');
this.summary;
this.optionsLists = this.element.find('.options-list');
//bottom summary
this.fixedSummary = this.element.find('.cd-builder-footer');
this.modelPreview = this.element.find('.selected-product').find('img');
this.totPriceWrapper = this.element.find('.tot-price').find('b');
//builder navigations
this.mainNavigation = this.element.find('.cd-builder-main-nav');
this.secondaryNavigation = this.element.find('.cd-builder-secondary-nav');
//used to check if the builder content has been loaded properly
this.loaded = true;
// bind builder events
this.bindEvents();
}
ProductBuilder.prototype.bindEvents = function() {
var self = this;
//detect click on the left navigation
this.mainNavigation.on('click', 'li:not(.active)', function(event){
event.preventDefault();
self.loaded && self.newContentSelected($(this).index());
});
//detect click on bottom fixed navigation
this.secondaryNavigation.on('click', '.nav-item li:not(.buy)', function(event){
event.preventDefault();
var stepNumber = ( $(this).parents('.next').length > 0 ) ? $(this).index() + 1 : $(this).index() - 1;
self.loaded && self.newContentSelected(stepNumber);
});
//detect click on one element in an options list (e.g, models, accessories)
this.optionsLists.on('click', '.js-option', function(event){
self.updateListOptions($(this));
});
//detect clicks on customizer controls (e.g., colors ...)
this.stepsWrapper.on('click', '.cd-product-customizer a', function(event){
event.preventDefault();
self.customizeModel($(this));
});
};
ProductBuilder.prototype.newContentSelected = function(nextStep) {
//first - check if a model has been selected - user can navigate through the builder
if( this.fixedSummary.hasClass('disabled') ) {
//no model has been selected - show alert
this.fixedSummary.addClass('show-alert');
} else {
//model has been selected so show new content
//first check if the color step has been completed - in this case update the product bottom preview
if( this.steps.filter('.active').is('[data-selection="colors"]') ) {
//in this case, color has been changed - update the preview image
var imageSelected = this.steps.filter('.active').find('.cd-product-previews').children('.selected').children('img').attr('src');
this.modelPreview.attr('src', imageSelected);
}
//if Summary is the selected step (new step to be revealed) -> update summary content
if( nextStep + 1 >= this.steps.length ) {
this.createSummary();
}
this.showNewContent(nextStep);
this.updatePrimaryNav(nextStep);
this.updateSecondaryNav(nextStep);
}
}
ProductBuilder.prototype.showNewContent = function(nextStep) {
var actualStep = this.steps.filter('.active').index() + 1;
if( actualStep < nextStep + 1 ) {
//go to next section
this.steps.eq(actualStep-1).removeClass('active back').addClass('move-left');
this.steps.eq(nextStep).addClass('active').removeClass('move-left back');
} else {
//go to previous section
this.steps.eq(actualStep-1).removeClass('active back move-left');
this.steps.eq(nextStep).addClass('active back').removeClass('move-left');
}
}
ProductBuilder.prototype.updatePrimaryNav = function(nextStep) {
this.mainNavigation.find('li').eq(nextStep).addClass('active').siblings('.active').removeClass('active');
}
ProductBuilder.prototype.updateSecondaryNav = function(nextStep) {
( nextStep == 0 ) ? this.fixedSummary.addClass('step-1') : this.fixedSummary.removeClass('step-1');
this.secondaryNavigation.find('.nav-item.next').find('li').eq(nextStep).addClass('visible').removeClass('visited').prevAll().removeClass('visited').addClass('visited').end().nextAll().removeClass('visible visited');
this.secondaryNavigation.find('.nav-item.prev').find('li').eq(nextStep).addClass('visible').removeClass('visited').prevAll().removeClass('visited').addClass('visited').end().nextAll().removeClass('visible visited');
}
ProductBuilder.prototype.createSummary = function() {
var self = this;
this.steps.each(function(){
//this function may need to be updated according to your builder steps and summary
var step = $(this);
if( $(this).data('selection') == 'colors' ) {
//create the Color summary
var colorSelected = $(this).find('.cd-product-customizer').find('.selected'),
color = colorSelected.children('a').data('color'),
colorName = colorSelected.data('content'),
imageSelected = $(this).find('.cd-product-previews').find('.selected img').attr('src');
self.summary.find('.summary-color').find('.color-label').text(colorName).siblings('.color-swatch').attr('data-color', color);
self.summary.find('.product-preview').attr('src', imageSelected);
} else if( $(this).data('selection') == 'accessories' ) {
var selectedOptions = $(this).find('.js-option.selected'),
optionsContent = '';
if( selectedOptions.length == 0 ) {
optionsContent = '<li><p>No Accessories selected;</p></li>';
} else {
selectedOptions.each(function(){
optionsContent +='<li><p>'+$(this).find('p').text()+'</p></li>';
});
}
self.summary.find('.summary-accessories').children('li').remove().end().append($(optionsContent));
}
});
}
ProductBuilder.prototype.updateListOptions = function(listItem) {
var self = this;
if( listItem.hasClass('js-radio') ) {
//this means only one option can be selected (e.g., models) - so check if there's another option selected and deselect it
var alreadySelectedOption = listItem.siblings('.selected'),
price = (alreadySelectedOption.length > 0 ) ? -Number(alreadySelectedOption.data('price')) : 0;
//if the option was already selected and you are deselecting it - price is the price of the option just clicked
( listItem.hasClass('selected') )
? price = -Number(listItem.data('price'))
: price = Number(listItem.data('price')) + price;
//now deselect all the other options
alreadySelectedOption.removeClass('selected');
//toggle the option just selected
listItem.toggleClass('selected');
//update totalPrice - only if the step is not the Models step
(listItem.parents('[data-selection="models"]').length == 0) && self.updatePrice(price);
} else {
//more than one options can be selected - just need to add/remove the one just clicked
var price = ( listItem.hasClass('selected') ) ? -Number(listItem.data('price')) : Number(listItem.data('price'));
//toggle the option just selected
listItem.toggleClass('selected');
//update totalPrice
self.updatePrice(price);
}
if( listItem.parents('[data-selection="models"]').length > 0 ) {
//since a model has been selected/deselected, you need to update the builder content
self.updateModelContent(listItem);
}
};
ProductBuilder.prototype.updateModelContent = function(model) {
var self = this;
if( model.hasClass('selected') ) {
var modelType = model.data('model'),
modelImage = model.find('img').attr('src');
//need to update the product image in the bottom fixed navigation
this.modelPreview.attr('src', modelImage);
//need to update the content of the builder according to the selected product
//first - remove the contet which refers to a different model
this.models.siblings('li').remove();
//second - load the new content
$.ajax({
type : "GET",
dataType : "html",
url : modelType+".html",
beforeSend : function(){
self.loaded = false;
model.siblings().removeClass('loaded');
},
success : function(data){
self.models.after(data);
self.loaded = true;
model.addClass('loaded');
//activate top and bottom navigations
self.fixedSummary.add(self.mainNavigation).removeClass('disabled show-alert');
//update properties of the object
self.steps = self.element.find('.builder-step');
self.summary = self.element.find('[data-selection="summary"]');
//detect click on one element in an options list
self.optionsLists.off('click', '.js-option');
self.optionsLists = self.element.find('.options-list');
self.optionsLists.on('click', '.js-option', function(event){
self.updateListOptions($(this));
});
//this is used not to load the animation the first time new content is loaded
self.element.find('.first-load').removeClass('first-load');
},
error : function(jqXHR, textStatus, errorThrown) {
//you may want to show an error message here
}
});
//update price (no adding/removing)
this.totPriceWrapper.text(model.data('price'));
} else {
//no model has been selected
this.fixedSummary.add(this.mainNavigation).addClass('disabled');
//update price
this.totPriceWrapper.text('0');
this.models.find('.loaded').removeClass('loaded');
}
};
ProductBuilder.prototype.customizeModel = function(target) {
var parent = target.parent('li')
index = parent.index();
//update final price
var price = ( parent.hasClass('selected') )
? 0
: Number(parent.data('price')) - parent.siblings('.selected').data('price');
this.updatePrice(price);
target.parent('li').addClass('selected').siblings().removeClass('selected').parents('.cd-product-customizer').siblings('.cd-product-previews').children('.selected').removeClass('selected').end().children('li').eq(index).addClass('selected');
};
ProductBuilder.prototype.updatePrice = function(price) {
var actualPrice = Number(this.totPriceWrapper.text()) + price;
this.totPriceWrapper.text(actualPrice);
};
if( $('.cd-product-builder').length > 0 ) {
$('.cd-product-builder').each(function(){
//create a productBuilder object for each .cd-product-builder
new ProductBuilder($(this));
});
}
});
Changing the JS/Css, but no luck.
Related
I'm using an existing code, written not by me and I have a very limited experience in Java. The code creates a filtered gallery with 15 pics per page using isotope.pkgd.min. Each pic can be opened in a popup, magnific popup.
The problem: I have a filter item e.g. nature with 30-40 pics. The code creates two pages with 15 items per page. However also the popup shows only 15 items and not 30-40. What do I need to do, in order to show 30-40 items in popup but keep 15 items per page in the gallery? Many thanks in advance for your help
$(document).ready(function () {
/*************** Gallery ******************/
var itemSelector = ".tm-gallery-item";
var responsiveIsotope = [ [480, 4], [720, 6] ];
var itemsPerPageDefault = 15;
var itemsPerPage = defineItemsPerPage();
var currentNumberPages = 1;
var currentPage = 1;
var currentFilter = '*';
var filterValue = "";
var pageAttribute = 'data-page';
var pagerClass = 'isotope-pager';
var $container = $('.tm-gallery-container').isotope({
itemSelector: itemSelector
});
function adjustGalleryLayout(currentPopup) {
if(currentPopup == 'gallery') {
// layout Isotope after each image loads
$container.imagesLoaded().progress( function() {
$container.isotope('layout');
});
}
}
/************** Popup *****************/
$('#inline-popups').magnificPopup({
delegate: 'a',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
beforeOpen: function() {
this.st.mainClass = this.st.el.attr('data-effect');
},
open: function() {
adjustGalleryLayout($.magnificPopup.instance.content.attr('id'));
}
},
midClick: true, // allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source.
showCloseBtn: false
});
$('.tm-close-popup').on( "click", function() {
$.magnificPopup.close();
});
var popupInstance = $.magnificPopup.instance;
$('.tm-btn-next').on("click", function(e) {
popupInstance.next();
adjustGalleryLayout(popupInstance.content.attr('id'));
});
$('.tm-btn-contact').on("click", function(e) {
popupInstance.goTo(4);
})
// update items based on current filters
function changeFilter(selector) { $container.isotope({ filter: selector });
$(selector).magnificPopup({
delegate: 'a',
type:'image',
gallery:{
enabled:true
}
})
}
// grab all checked filters and goto page on fresh isotope output
function goToPage(n) {
currentPage = n;
var selector = itemSelector;
var exclusives = [];
if(currentFilter != '*') {
exclusives.push(selector + '.' + currentFilter);
}
// smash all values back together for 'and' filtering
filterValue = exclusives.length ? exclusives.join('') : '*';
a=exclusives;
// add page number to the string of filters
var wordPage = currentPage.toString();
filterValue += ('.'+wordPage);
changeFilter(filterValue);
}
// determine page breaks based on window width and preset values
function defineItemsPerPage() {
var pages = itemsPerPageDefault;
for( var i = 0; i < responsiveIsotope.length; i++ ) {
if( $(window).width() <= responsiveIsotope[i][0] ) {
pages = responsiveIsotope[i][1];
break;
}
}
return pages;
}
function setPagination() {
var SettingsPagesOnItems = function(){
var itemsLength = $container.children(itemSelector).length;
var pages = Math.ceil(itemsLength / itemsPerPage);
var item = 1;
var page = 1;
var selector = itemSelector;
var exclusives = [];
if(currentFilter != '*') {
exclusives.push(selector + '.' + currentFilter);
}
// smash all values back together for 'and' filtering
filterValue = exclusives.length ? exclusives.join('') : '*';
// find each child element with current filter values
$container.children(filterValue).each(function(){
// increment page if a new one is needed
if( item > itemsPerPage ) {
page++;
item = 1;
}
// add page number to element as a class
wordPage = page.toString();
var classes = $(this).attr('class').split(' ');
var lastClass = classes[classes.length-1];
// last class shorter than 4 will be a page number, if so, grab and replace
if(lastClass.length < 4){
$(this).removeClass();
classes.pop();
classes.push(wordPage);
classes = classes.join(' ');
$(this).addClass(classes);
} else {
// if there was no page number, add it
$(this).addClass(wordPage);
}
item++;
});
currentNumberPages = page;
}();
// create page number navigation
var CreatePagers = function() {
var $isotopePager = ( $('.'+pagerClass).length == 0 ) ? $('<div class="'+pagerClass+' tm-paging"></div>') : $('.'+pagerClass);
$isotopePager.html('');
if(currentNumberPages > 1){
for( var i = 0; i < currentNumberPages; i++ ) {
var $pager = '';
if(currentPage == i+1) {
$pager = $('');
} else {
$pager = $('');
}
$pager.html(i+1);
$pager.click(function(){
$('.tm-paging-link').removeClass('active');
$(this).addClass('active');
var page = $(this).eq(0).attr(pageAttribute);
goToPage(page);
});
$pager.appendTo($isotopePager);
}
}
$container.after($isotopePager);
}();
}
setPagination();
goToPage(1);
//event handlers
$('.tm-gallery-link').click(function(e) {
var filter = $(this).data('filter');
currentFilter = filter;
setPagination();
goToPage(1);
$('.tm-gallery-link').removeClass('active');
$(e.target).addClass('active');
})
//Handle window resize
$(window).resize(function(){
itemsPerPage = defineItemsPerPage();
setPagination();
goToPage(1);
});
});
I'm trying to update the button as it counts down to a link being live, but the button text is not being updated...
here's my script
var meetings_default_view_default = {
start: function () {
window.setInterval(function () {
var _div = $('.meetings_wrapper');
var _meeting_id = [];
var _url = _div.data('url');
// Collect all the relevant meeting ids
_div.children().each(function () {
_meeting_id.push($(this).data('id'));
});
if (!_meeting_id.length) {
return;
}
$.post(_url, {meeting_id: _meeting_id}, function(data) {
if (data.alert) {
alert(data.alert);
return false
};
// if (!data.meeting_id) {
// alert('No data returned');
// }
for (var i in data.meeting_id) {
var meeting_data = data.meeting_id[ i ];
var meeting_id = meeting_data[ 'id' ];
// locate the start button *within* the panel with the appropriate id
var _button = _div.find('.panel[data-id=' + meeting_id + '] a.button').last();
var _css = 'button';
if (meeting_data['css_class']) {
_css += ' ' + meeting_data['css_class'];
}
_button.attr('class',_css);
if (meeting_data['label']) {
_button.find('span').text(meeting_data['label']);
}
}
});
}, 60000);
}
};
$(meetings_default_view_default.start);
everything else ( css ) changes, just not the text of the button ? been staring at this for ages now !
sorry, another silly mistake when tired. This works fine...
if (meeting_data['label']) {
_button.text(meeting_data['label']);
}
Even better - this does not overwrite the tags in the button element it just updates the text part.
if (meeting_data['label']) {
_button.textContent = meeting_data['label'];
}
I use a plugin that use this block of statements to display menus,Now I use it in angular js.
(function(window) {
'use strict';
var support = { animations : Modernizr.cssanimations },
animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' },
animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ],
onEndAnimation = function( el, callback ) {
var onEndCallbackFn = function( ev ) {
if( support.animations ) {
if( ev.target != this ) return;
this.removeEventListener( animEndEventName, onEndCallbackFn );
}
if( callback && typeof callback === 'function' ) { callback.call(); }
};
if( support.animations ) {
el.addEventListener( animEndEventName, onEndCallbackFn );
}
else {
onEndCallbackFn();
}
};
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function MLMenu(el, options) {
this.el = el;
this.options = extend( {}, this.options );
extend( this.options, options );
// the menus (<ul>´s)
this.menus = [].slice.call(this.el.querySelectorAll('.menu__level'));
// index of current menu
this.current = 0;
this._init();
}
MLMenu.prototype.options = {
// show breadcrumbs
breadcrumbsCtrl : true,
// initial breadcrumb text
initialBreadcrumb : 'all',
// show back button
backCtrl : true,
// delay between each menu item sliding animation
itemsDelayInterval : 60,
// direction
direction : 'r2l',
// callback: item that doesn´t have a submenu gets clicked
// onItemClick([event], [inner HTML of the clicked item])
onItemClick : function(ev, itemName) { return false; }
};
MLMenu.prototype._init = function() {
// iterate the existing menus and create an array of menus, more specifically an array of objects where each one holds the info of each menu element and its menu items
this.menusArr = [];
var self = this;
this.menus.forEach(function(menuEl, pos) {
var menu = {menuEl : menuEl, menuItems : [].slice.call(menuEl.querySelectorAll('.menu__item'))};
self.menusArr.push(menu);
// set current menu class
if( pos === self.current ) {
classie.add(menuEl, 'menu__level--current');
}
});
// create back button
if( this.options.backCtrl ) {
this.backCtrl = document.createElement('button');
this.backCtrl.className = 'menu__back menu__back--hidden';
this.backCtrl.setAttribute('aria-label', 'Go back');
this.backCtrl.innerHTML = '<span class="icon icon--arrow-left"></span>';
this.el.insertBefore(this.backCtrl, this.el.firstChild);
}
// create breadcrumbs
if( self.options.breadcrumbsCtrl ) {
this.breadcrumbsCtrl = document.createElement('nav');
this.breadcrumbsCtrl.className = 'menu__breadcrumbs';
this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild);
// add initial breadcrumb
this._addBreadcrumb(0);
}
// event binding
this._initEvents();
};
MLMenu.prototype._initEvents = function() {
var self = this;
for(var i = 0, len = this.menusArr.length; i < len; ++i) {
this.menusArr[i].menuItems.forEach(function(item, pos) {
item.querySelector('a').addEventListener('click', function(ev) {
var submenu = ev.target.getAttribute('data-submenu'),
itemName = ev.target.innerHTML,
subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]');
// check if there's a sub menu for this item
if( submenu && subMenuEl ) {
ev.preventDefault();
// open it
self._openSubMenu(subMenuEl, pos, itemName);
}
else {
// add class current
var currentlink = self.el.querySelector('.menu__link--current');
if( currentlink ) {
classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current');
}
classie.add(ev.target, 'menu__link--current');
// callback
self.options.onItemClick(ev, itemName);
}
});
});
}
// back navigation
if( this.options.backCtrl ) {
this.backCtrl.addEventListener('click', function() {
self._back();
});
}
};
MLMenu.prototype._openSubMenu = function(subMenuEl, clickPosition, subMenuName) {
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
// save "parent" menu index for back navigation
this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current;
// save "parent" menu´s name
this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName;
// current menu slides out
this._menuOut(clickPosition);
// next menu (submenu) slides in
this._menuIn(subMenuEl, clickPosition);
};
MLMenu.prototype._back = function() {
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
// current menu slides out
this._menuOut();
// next menu (previous menu) slides in
var backMenu = this.menusArr[this.menusArr[this.current].backIdx].menuEl;
this._menuIn(backMenu);
// remove last breadcrumb
if( this.options.breadcrumbsCtrl ) {
this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild);
}
};
MLMenu.prototype._menuOut = function(clickPosition) {
// the current menu
var self = this,
currentMenu = this.menusArr[this.current].menuEl,
isBackNavigation = typeof clickPosition == 'undefined' ? true : false;
// slide out current menu items - first, set the delays for the items
this.menusArr[this.current].menuItems.forEach(function(item, pos) {
item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms';
});
// animation class
if( this.options.direction === 'r2l' ) {
classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
}
else {
classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
}
};
MLMenu.prototype._menuIn = function(nextMenuEl, clickPosition) {
var self = this,
// the current menu
currentMenu = this.menusArr[this.current].menuEl,
isBackNavigation = typeof clickPosition == 'undefined' ? true : false,
// index of the nextMenuEl
nextMenuIdx = this.menus.indexOf(nextMenuEl),
nextMenuItems = this.menusArr[nextMenuIdx].menuItems,
nextMenuItemsTotal = nextMenuItems.length;
// slide in next menu items - first, set the delays for the items
nextMenuItems.forEach(function(item, pos) {
item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms';
// we need to reset the classes once the last item animates in
// the "last item" is the farthest from the clicked item
// let's calculate the index of the farthest item
var farthestIdx = clickPosition <= nextMenuItemsTotal/2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0;
if( pos === farthestIdx ) {
onEndAnimation(item, function() {
// reset classes
if( self.options.direction === 'r2l' ) {
classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
else {
classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
classie.remove(currentMenu, 'menu__level--current');
classie.add(nextMenuEl, 'menu__level--current');
//reset current
self.current = nextMenuIdx;
// control back button and breadcrumbs navigation elements
if( !isBackNavigation ) {
// show back button
if( self.options.backCtrl ) {
classie.remove(self.backCtrl, 'menu__back--hidden');
}
// add breadcrumb
self._addBreadcrumb(nextMenuIdx);
}
else if( self.current === 0 && self.options.backCtrl ) {
// hide back button
classie.add(self.backCtrl, 'menu__back--hidden');
}
// we can navigate again..
self.isAnimating = false;
});
}
});
// animation class
if( this.options.direction === 'r2l' ) {
classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
else {
classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
};
MLMenu.prototype._addBreadcrumb = function(idx) {
if( !this.options.breadcrumbsCtrl ) {
return false;
}
var bc = document.createElement('a');
bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb;
this.breadcrumbsCtrl.appendChild(bc);
var self = this;
bc.addEventListener('click', function(ev) {
ev.preventDefault();
// do nothing if this breadcrumb is the last one in the list of breadcrumbs
if( !bc.nextSibling || self.isAnimating ) {
return false;
}
self.isAnimating = true;
// current menu slides out
self._menuOut();
// next menu slides in
var nextMenu = self.menusArr[idx].menuEl;
self._menuIn(nextMenu);
// remove breadcrumbs that are ahead
var siblingNode;
while (siblingNode = bc.nextSibling) {
self.breadcrumbsCtrl.removeChild(siblingNode);
}
});
};
window.MLMenu = MLMenu;
})(window);
and customized java script :
(function() {
var menuEl = document.getElementById('ml-menu');
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl : false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
//mobile menu toggle
var openMenuCtrl = document.querySelector('.action--open'),
closeMenuCtrl = document.querySelector('.action--close');
openMenuCtrl.addEventListener('click', openMenu);
closeMenuCtrl.addEventListener('click', closeMenu);
function openMenu() {
classie.add(menuEl, 'menu--open');
}
function closeMenu() {
classie.remove(menuEl, 'menu--open');
}
// simulate grid content loading
var gridWrapper = document.querySelector('.content');
function loadDummyData(ev, itemName) {
ev.preventDefault();
closeMenu();
gridWrapper.innerHTML = '';
classie.add(gridWrapper, 'content--loading');
setTimeout(function() {
classie.remove(gridWrapper, 'content--loading');
gridWrapper.innerHTML = '<ul class="products">' + dummyData[itemName] + '<ul>';
}, 700);
}
})();
I want convert a part of this below customized script to angular js.
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl : false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
I use it as this in angular but it has error .
$scope.menuEl = document.getElementById('ml-menu');
var menuEl = document.getElementById('ml-menu'),
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl: false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: $scope.loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
How can I do it ?
I think you should wrap your plugin with AngularJS directive https://docs.angularjs.org/guide/directive
I.e. create directive that uses your plugin and initialize it on marked DOM.
angular.module('yourModule')
.directive('mlMenu', function() {
return {
restrict: 'A',
link: function (scope, element) {
var mlmenu = new MLMenu(element,
...
}
};
});
Than in HTML
<div data-ml-menu></div>
And if you are going to modify scope in plugin listeners do not forget to use scope.$apply() wrapper.
I am using infinite scroll to load posts and tried to integrate a custom like button for every single posts that needs a small jquery script to work. My problem is I added this Jquery directly after the sucess in ajax load posts. But when I load eg the 3. page my jquery script executes twice and on the posts of the 2nd page the lke buttons are not working correctly. How can I handle this? If I dont execute the code after the ajax request and only call this jquery code globally the like buttons do not work in the new loaded posts of the ajax infinite scroll. Maybe I need to stop the sript of before when eg loadin 3. page through the ajax infinite scroll but how? This is my code:
function load_more_posts(selector){
var url = $(selector).attr('href');
var data;
loading = true;
$.ajax({
url: url,
data: data,
success: function( data ) {
var $items = $( '.loading-content .item', data );
$new_anchor = $( selector, data );
$items.addClass('hidden');
if ( $('#cooked-plugin-page .result-section.masonry-layout .loading-content').length ){
$( '.loading-content').isotope( 'insert', $items );
} else {
$( '.loading-content').append($items);
setTimeout(function() {
$items.removeClass('hidden');
}, 200);
}
if($new_anchor.length) {
$(selector).attr('href', $new_anchor.attr('href'));
} else {
$(selector).remove();
}
loading = false;
$('.like-btn').each(function() {
var $button = $(this),
$icon = $button.find('> i'),
likedRecipes = $.cookie('cpLikedRecipes'),
recipeID = $button.attr('data-recipe-id');
cookied = $button.attr('data-cookied');
userLiked = $button.attr('data-userliked');
if ( cookied == 1 && typeof likedRecipes !== 'undefined' && likedRecipes.split(',').indexOf(recipeID) > -1 || userLiked == 1 ) {
$icon.removeClass('fa-heart-o').addClass('fa-heart');
}
});
$('#cooked-plugin-page .like-btn').on('click', function() {
var $button = $(this),
$icon = $button.find('> i'),
$count = $button.find('.like-count'),
count = parseInt($count.text()),
likedRecipes = $.cookie('cpLikedRecipes'),
recipeID = $button.attr('data-recipe-id'),
cookied = $button.attr('data-cookied'),
likeURL = $button.attr('href'),
likeAction;
if ( $icon.hasClass('fa-heart-o') ) {
$icon.removeClass('fa-heart-o').addClass('fa-heart');
count++;
if (cookied == 1){
if ( typeof likedRecipes === 'undefined' ) {
likedRecipes = recipeID;
} else {
likedRecipes = likedRecipes + ',' + recipeID;
}
$.cookie('cpLikedRecipes', likedRecipes, { expires: 365 } );
}
likeAction = 'like';
} else {
$icon.removeClass('fa-heart').addClass('fa-heart-o');
count--;
if (cookied == 1){
if ( typeof likedRecipes === 'undefied' ) {
return false;
}
}
if (cookied == 1){
var likedSplit = likedRecipes.split(','),
recipeIdx = likedSplit.indexOf(recipeID);
if ( recipeIdx > -1 ) {
likedSplit.splice( recipeIdx, 1 );
likedRecipes = likedSplit.join(',');
$.cookie('cpLikedRecipes', likedRecipes, { expires: 365 } );
likeAction = 'dislike';
}
} else {
likeAction = 'dislike';
}
}
$.ajax({
'url' : likeURL,
'data': {
'action' : 'cp_like',
'likeAction': likeAction
},
success: function(data) {
$count.text(data);
}
});
return false;
});
$('#cooked-plugin-page .tab-links a').on('click', function() {
var tab = $(this).attr('href');
if ( !$(this).is('.current') ){
$(this).addClass('current').siblings('.current').removeClass('current');
$('#cooked-plugin-page.fullscreen .tab').removeClass('current');
$(tab).addClass('current');
$win.scrollTop(0);
}
return false;
});
if($('.rating-holder').length) {
$('.rating-holder .rate')
.on('mouseenter', function() {
var $me = $(this);
var $parent = $me.parents('.rating-holder');
var my_index = $me.index();
var rated = $parent.attr('data-rated');
$parent.removeClass(function(index, css) {
return (css.match (/(^|\s)rate-\S+/g) || []).join(' ');
});
$parent.addClass('rate-' + (my_index + 1));
})
.on('mouseleave', function() {
var $me = $(this);
var $parent = $me.parents('.rating-holder');
var my_index = $me.index();
var rated = $parent.attr('data-rated');
$parent.removeClass(function(index, css) {
return (css.match (/(^|\s)rate-\S+/g) || []).join(' ');
});
if(rated !== undefined) {
$parent.addClass('rate-' + rated);
}
})
.on('click', function() {
var $me = $(this);
var $parent = $me.parents('.rating-holder');
var my_index = $me.index();
$('.rating-real-value').val(my_index + 1);
$parent.attr('data-rated', my_index + 1);
$parent.addClass('rate-' + (my_index + 1));
});
}
setTimeout(function() {
masonry();
}, 500);
}
});
}
There's a great plugin for scrolling. He has methods such as bund, unbind, destroy and e.t.c.:
https://github.com/infinite-scroll/infinite-scroll#methods
I have running script which moves the tr elements from one container to another on double click. But i have below mentioned issues:
1) If we do are very quick double-click on elements than it moves but its values doesn't come, it shows empty tags.
2) I want to change the background color on double click and it color should remove when we click outside or another elements.
<script>
$(function () {
function initTabelMgmt() {
selectInvitees();
moveSelectedInvitees();
deleteInvitees();
//scrollOpen();
}
var tmContainer = $("div.cv-tm-body");
var toggleAssignBtn = tmContainer.find('.cv-move-items button');
/*
function scrollOpen() {
var position = $('div.cv-item li.open').first().position();
var offsetTop = $('div.cv-tm-col-r .cv-helper-grid-overflow').scrollTop();
var unitHeight = $('div.cv-item li.open').first().height();
var containerHeight = $('div.cv-tm-col-r .cv-helper-grid-overflow').height();
var scrollAmount = offsetTop + position.top;
if ((offsetTop - position.top) <= 0 && (offsetTop - position.top) >= (-containerHeight + unitHeight)) {
//do nothing
} else {
$('div.cv-tm-col-r .cv-helper-grid-overflow').animate({
scrollTop: scrollAmount
});
}
};
*/
// scrollOpen end
function selectInvitees() {
//select items from invitee list
var startIndex, endIndex;
var dbclick = false;
tmContainer.find("table.cv-invitees").on('click', 'tr', function (e) {
var row = $(this);
setTimeout(function () {
//singleclick functionality start.
if (dbclick == false) {
if (!row.is('.assigned')) {
toggleAssignBtn.removeClass('is-disabled');
if (e.shiftKey) {
row.parents('.cv-invitees').find('tr').removeClass('selected');
endIndex = row.parents('.cv-invitees').find('tr').index(this);
var range = row.closest('table').find('tr').slice(Math.min(startIndex, endIndex), Math.max(startIndex, endIndex) + 1).not('.assigned');
range.addClass('selected');
} else if (e.ctrlKey) {
startIndex = row.parents('.cv-invitees').find('tr').index(this);
row.toggleClass('selected');
} else {
startIndex = row.parents('.cv-invitees').find('tr').index(this);
row.parents('.cv-invitees').find('tr').not(this).removeClass('selected');
row.toggleClass('selected');
}
}
}
}, 200)
})
.dblclick(function () {
dbclick = true
//doubleclick functionality start.
toggleAssignBtn.addClass('is-disabled');
function moveSelectedInviteesDBClick() {
var row = tmContainer.find("table.cv-invitees tr.selected");
if (!row.is('.assigned')) {
var allOpenSeat = $('.cv-item .open');
var numberOpen = allOpenSeat.length;
var name = row.find("td").eq(0).text();;
var company = row.find("td").eq(1).text();
var addedInvitees = [];
allOpenSeat.each(function (index) {
if (index < 1) {
var openSeat = $(this);
openSeat.find('.name').text(name);
if (company != '') {
openSeat.find('.company').addClass('show').text(company);
}
var seatAssignment = new Object();
seatAssignment.company = "";
addedInvitees.push(seatAssignment);
openSeat.removeClass('open');
}
row.remove();
});
}
} // moveSelectedInviteesDBClick
moveSelectedInviteesDBClick();
setTimeout(function () {
dbclick = false
}, 300)
});
} // selectInvitees end
function moveSelectedInvitees() {
//move invitees from left to right
tmContainer.find('button.cvf-moveright').click(function () {
var selectedItem = $('.cv-invitees .selected');
var allOpenSeat = $('.cv-item .open');
var numberSelected = selectedItem.length;
var numberOpen = allOpenSeat.length;
var errorMsg = tmContainer.prev('.cv-alert-error');
if (numberSelected > numberOpen) {
errorMsg.removeClass('is-hidden');
} else {
var name;
var company;
var invitee = [];
var selectedInvitees = [];
var count = 0;
selectedItem.each(function () {
var $this = $(this);
name = $this.find("td").eq(0).text();
company = $this.find("td").eq(1).text();
invitee = [name, company];
selectedInvitees.push(invitee);
count = count + 1;
i = 0;
$this.remove();
});
var addedInvitees = [];
var items = $('div.cv-item li');
var seatItems = $('div.cv-order li');
allOpenSeat.each(function (index) {
if (index < count) {
var openSeat = $(this);
openSeat.find('.name').text(selectedInvitees[index][0]);
if (selectedInvitees[index][1] != '') {
openSeat.find('.company').addClass('show').text(selectedInvitees[index][1]);
}
var seatAssignment = new Object();
seatAssignment.company = "";
addedInvitees.push(seatAssignment);
//selectedInvitees.shift();
openSeat.removeClass('open');
}
});
selectedInvitees = [];
}
toggleAssignBtn.addClass('is-disabled');
});
} // moveSelectedInvitees end
function deleteInvitees() {
//move invitees from left to right
tmContainer.find('div.cv-tm-col-r .cv-icon-remove').click(function () {
//delete seat assignment
var icon = $(this);
var idx = $('.ui-sortable li').index(icon.parent());
icon.parent().fadeTo(0, 0).addClass('open').find('.name').text('Open').end().fadeTo(750, 1);
icon.parent().find('.company').removeClass('show').text('');
// icon.parent().find('.entitystub').text('00000000-0000-0000-0000-000000000000');
// icon.parent().find('.entitytype').text('0');
// icon.parent().find('.pipe').remove();
// icon.hide();
// var testSeat = $('.seat-numbers li').get(idx);
//var seatStub = j$.trim(j$(testSeat).find('.seatstub').text());
//var input = { 'seatStub': seatStub };
//AssignSeats(input, "/Subscribers/WS/SeatAssignmentService.asmx/DeleteRegistrant");
});
}
initTabelMgmt();
}); // document.ready end
</script>
Your code looks pretty nice. You should also use in order to register from jQuery a single click event the native method .click(...). So please change the following line
tmContainer.find("table.cv-invitees").on('click', 'tr', function (e) {
To:
tmContainer.find("table.cv-invitees").click(function (e) {
and everything should work fine. For some strange reasons the function
$("#someelement").on("click", ...);
does not work always, only sometimes. JQuery officially recommends you to use the native functions for predefined events (such as onclick, onkeyup, onchange etc.) because of this strange behavior.
Edit:
If dblick does not work now, then make 2 lines please, like this:
tmContainer.find("table.cv-invitees").click(function (e) {
// [...]
;
tmContainer.find("table.cv-invitees").dbclick(function (e) {
// [...]
Edit2:
If it does not work, too, then please remove the single click event listener when you are in the .click() closure. Because if this happens, jQuery´s behavior is to treat it always as a single click. So, in other words dblick() will never be triggered, because .click() will always happens before. And then jQuery won´t count up to 2 fast clicks. Expect the unexpected^^
Edit3: This is the full code, which should hopefully work now as it is:
$(function ()
{
function initTabelMgmt()
{
selectInvitees();
moveSelectedInvitees();
deleteInvitees();
//scrollOpen();
}
var tmContainer = $("div.cv-tm-body");
var toggleAssignBtn = tmContainer.find('.cv-move-items button');
var iClickCounter = 0;
var dtFirstClick, dtSecondClick;
/*
function scrollOpen() {
var position = $('div.cv-item li.open').first().position();
var offsetTop = $('div.cv-tm-col-r .cv-helper-grid-overflow').scrollTop();
var unitHeight = $('div.cv-item li.open').first().height();
var containerHeight = $('div.cv-tm-col-r .cv-helper-grid-overflow').height();
var scrollAmount = offsetTop + position.top;
if ((offsetTop - position.top) <= 0 && (offsetTop - position.top) >= (-containerHeight + unitHeight)) {
//do nothing
} else {
$('div.cv-tm-col-r .cv-helper-grid-overflow').animate({
scrollTop: scrollAmount
});
}
};
*/
// scrollOpen end
function selectInvitees()
{
//select items from invitee list
var startIndex, endIndex;
var dbclick = false;
tmContainer.find("table.cv-invitees").click(function(e)
{
iClickCounter++;
if (iClickCounter === 1)
{
dtFirstClick = new Date();
var row = $(this);
window.setTimeout(function ()
{
//singleclick functionality start.
if (dbclick == false)
{
if (!row.is('.assigned'))
{
toggleAssignBtn.removeClass('is-disabled');
if (e.shiftKey)
{
row.parents('.cv-invitees').find('tr').removeClass('selected');
endIndex = row.parents('.cv-invitees').find('tr').index(this);
var range = row.closest('table').find('tr').slice(Math.min(startIndex, endIndex), Math.max(startIndex, endIndex) + 1).not('.assigned');
range.addClass('selected');
}
else if (e.ctrlKey)
{
startIndex = row.parents('.cv-invitees').find('tr').index(this);
row.toggleClass('selected');
}
else
{
startIndex = row.parents('.cv-invitees').find('tr').index(this);
row.parents('.cv-invitees').find('tr').not(this).removeClass('selected');
row.toggleClass('selected');
}
}
}
},
200);
}
else if (iClickCounter === 2)
{
dtSecondClick = new Date();
}
else if (iClickCounter === 3)
{
if (dtSecondClick.getTime() - dtFirstClick.getTime() < 1000)
{
return;
}
iClickCounter = 0;
dbclick = true
//doubleclick functionality start.
toggleAssignBtn.addClass('is-disabled');
function moveSelectedInviteesDBClick()
{
var row = tmContainer.find("table.cv-invitees tr.selected");
if (!row.is('.assigned'))
{
var allOpenSeat = $('.cv-item .open');
var numberOpen = allOpenSeat.length;
var name = row.find("td").eq(0).text();;
var company = row.find("td").eq(1).text();
var addedInvitees = [];
allOpenSeat.each(function (index)
{
if (index < 1)
{
var openSeat = $(this);
openSeat.find('.name').text(name);
if (company != '') {
openSeat.find('.company').addClass('show').text(company);
}
var seatAssignment = new Object();
seatAssignment.company = "";
addedInvitees.push(seatAssignment);
openSeat.removeClass('open');
}
row.remove();
}
);
}
}
// moveSelectedInviteesDBClick
moveSelectedInviteesDBClick();
window.setTimeout(function ()
{
dbclick = false
}, 300);
}
}
);
} // selectInvitees end
function moveSelectedInvitees()
{
//move invitees from left to right
tmContainer.find('button.cvf-moveright').click(function ()
{
var selectedItem = $('.cv-invitees .selected');
var allOpenSeat = $('.cv-item .open');
var numberSelected = selectedItem.length;
var numberOpen = allOpenSeat.length;
var errorMsg = tmContainer.prev('.cv-alert-error');
if (numberSelected > numberOpen) {
errorMsg.removeClass('is-hidden');
}
else
{
var name;
var company;
var invitee = [];
var selectedInvitees = [];
var count = 0;
selectedItem.each(function () {
var $this = $(this);
name = $this.find("td").eq(0).text();
company = $this.find("td").eq(1).text();
invitee = [name, company];
selectedInvitees.push(invitee);
count = count + 1;
i = 0;
$this.remove();
});
var addedInvitees = [];
var items = $('div.cv-item li');
var seatItems = $('div.cv-order li');
allOpenSeat.each(function (index)
{
if (index < count)
{
var openSeat = $(this);
openSeat.find('.name').text(selectedInvitees[index][0]);
if (selectedInvitees[index][1] != '')
{
openSeat.find('.company').addClass('show').text(selectedInvitees[index][1]);
}
var seatAssignment = new Object();
seatAssignment.company = "";
addedInvitees.push(seatAssignment);
//selectedInvitees.shift();
openSeat.removeClass('open');
}
}
);
selectedInvitees = [];
}
toggleAssignBtn.addClass('is-disabled');
}
);
} // moveSelectedInvitees end
function deleteInvitees()
{
//move invitees from left to right
tmContainer.find('div.cv-tm-col-r .cv-icon-remove').click(function ()
{
//delete seat assignment
var icon = $(this);
var idx = $('.ui-sortable li').index(icon.parent());
icon.parent().fadeTo(0, 0).addClass('open').find('.name').text('Open').end().fadeTo(750, 1);
icon.parent().find('.company').removeClass('show').text('');
// icon.parent().find('.entitystub').text('00000000-0000-0000-0000-000000000000');
// icon.parent().find('.entitytype').text('0');
// icon.parent().find('.pipe').remove();
// icon.hide();
// var testSeat = $('.seat-numbers li').get(idx);
//var seatStub = j$.trim(j$(testSeat).find('.seatstub').text());
//var input = { 'seatStub': seatStub };
//AssignSeats(input, "/Subscribers/WS/SeatAssignmentService.asmx/DeleteRegistrant");
}
);
}
initTabelMgmt();
}
); // document.ready end
I guess that you interpret in your special case a double click as 3 times clicked at the same table entry. And if a user do so and if the time difference between first and second click is longer than one second, a double click will be fired. I think should be the solution to deal with this special case.
Edit 4: Please test, if it is possible to click on 3 different table column and get also double click fired. I think this is an disadvantage on how my code handles the double click. So, you need to know from which table column you have already 1 to 3 clicks set. How can we do this? Basically, there are 3 possibilities to do this:
(HTML5 only:) Make data attribute on each tr and the value for this data attribute
should be the clicks already clicke on this tr.
Define a global object key/value pair object, which holds the
event-ID (but I don´t know how to get this back by jQuery driven
events) as the key and the amount of clicks already done as the
value. And then if you are on the next click, you can decide what is
to do now for this tr. This is my favorite alternative!
Last but not least: Only register the click event on every tr and
make for each click-registering an own global area, so that we just
avoid the actual problem. You can do this e. g. by making an JS
Object which hold a member variable as the iclickCounter and you
make a new object of this class, each time a new click event is
registered. But this alternative need a lot more code and is main-memory-hungry.
All of thes possible options need a wrap around your click event, e. g. a loop, that iterates over all tr elements in the given table. You did this already partially by calling the jQuery-function .find(..). This executes the closure on every found html element. So, in your case on all tr elements in the searched table. But what you need to do is to make the workaround of one of my options given above.