jQuery mouseenter only works on first page load - javascript

This code presents a grid of colored cells that change color on mouseenter, leaving a trail of cells of the new color. A button reloads the grid with cells of the original color. Problem is the mouseenter color change works only after the first grid load (on page refresh) but not on subsequent loads after the Create New Grid button is clicked.
I am new to javascript and jQuery and though I've been over the class materials and read some articles on how to do various parts of this, I cannot see what is wrong.
Visit the jsfiddle here.
var gridWidth = 600;
var fillGrid = function(){
$('.grid').empty();
var cellsPer = prompt('How many cells would you like in a side?');
console.log('cellsPer = ' + cellsPer);
var cellWidth = (gridWidth / cellsPer) - 1;
console.log('cellSize = ' + cellWidth);
var cell = "<div class='cell'></div>";
for (var i = 0; i < cellsPer**2; i++) {
$('.grid').append(cell);
};
$('.cell').css({
'background':'blue','height': cellWidth+'px', 'width': cellWidth+'px',
'float':'left','margin': '0 1px 1px 0'
});
};
$(document).ready(function() {
fillGrid();
$('.grid').css({'width': gridWidth+'px'});
$('button').click(function(){
fillGrid();
});
$('.cell').mouseenter(function() {
$(this).css('background','pink');
});
});

You where adding the mouseenter event listener only once on $(document).ready.
When fillGrid() gets called, a new set of '.cell' elements not bound to the mouseenter event get added to DOM.
You must tell them to behave the same again.
See the following snipped:
var gridWidth = 600;
var fillGrid = function(){
$('.grid').empty();
var cellsPer = prompt('How many cells would you like in a side?');
console.log('cellsPer = ' + cellsPer);
var cellWidth = (gridWidth / cellsPer) - 1;
console.log('cellSize = ' + cellWidth);
var cell = "<div class='cell'></div>";
for (var i = 0; i < cellsPer**2; i++) {
$('.grid').append(cell);
};
$('.cell').css({
'background':'blue','height': cellWidth+'px', 'width': cellWidth+'px',
'float':'left','margin': '0 1px 1px 0'
});
$('.cell').mouseenter(function() {
$(this).css('background','pink');
});
};
$(document).ready(function() {
fillGrid();
$('.grid').css({'width': gridWidth+'px'});
$('button').click(function(){
fillGrid();
});
});
button{
display:block;
margin:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Create New Grid</button>
<div class="grid"></div>

Move it outside $(document).ready function and add the mouse enter functionality as
' $(document).on('mouseenter','.cell',function() {
$(this).css('background','pink');
});'

Two issues I notice with this code
1) cellsPer ** 2 is not valid. Use Math.pow or cellsPer * cellsPer
2) You are only setting up the mouse enter listener on document.ready. Calling empty on your grid will remove all child elements - attached event listeners and all. This means you will need to re-add the event listeners every time you re-initialize the grid.
Here is an updated snippet with minimal changes to get your code working:
var gridWidth = 600;
var fillGrid = function(){
$('.grid').empty();
var cellsPer = prompt('How many cells would you like in a side?');
console.log('cellsPer = ' + cellsPer);
var cellWidth = (gridWidth / cellsPer) - 1;
console.log('cellSize = ' + cellWidth);
var cell = "<div class='cell'></div>";
for (var i = 0; i < cellsPer * cellsPer; i++) {
$('.grid').append(cell);
};
$('.cell').css({
'background':'blue','height': cellWidth+'px', 'width': cellWidth+'px',
'float':'left','margin': '0 1px 1px 0'
});
};
$(document).ready(function() {
fillGrid();
$('.grid').css({'width': gridWidth+'px'});
$('button').click(function(){
fillGrid();
$('.cell').mouseenter(function() {
$(this).css('background','pink');
});
});
$('.cell').mouseenter(function() {
$(this).css('background','pink');
});
});
button{
display:block;
margin:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Create New Grid</button>
<div class="grid"></div>
Since you're doing this for a class I'm not going to refactor the code but, one other suggestion I would make is adding the event listener in the fillGrid function, that way you neatly encapsulate everything that has to do with the grid in one place.

Related

function firing multiple times when scrolling fast

First of all, I'm not very advanced at code and tend to only do this part time so please excuse all terrible/ugly code! I appreciate there are already some solutions out there but I can't seem to make any of them work with my code so would really appreciate some help!
I'm using isotope grid and trying to setup an infinite scroll. I want to load 10 images at a time when the user scrolls to the bottom by taking these images from an array and appending them to a temp div.
This is working perfectly when scrolling slowly, but as soon as you scroll quickly the function seems to fire multiple times, it gets a little glitchy and loads lots of images at once.
$(window).scroll(function() {
var scrollTop = $(window).scrollTop();
var windowHeight = $(window).height();
var docuHeight = $(document).height();
if(scrollTop + windowHeight == docuHeight){
nextTenImages = imagesData.splice(0,10);
var content = ""
for (var i = 0; i < nextTenImages.length; i++) {
content +=
"<div class='box " + nextTenImages[i]["type"] + "'" + ">" +
"<div class='box-wrapper'>" +
"<img src='" + nextTenImages[i]["src"] + "' />" +
"</div>" +
"</div>"
};
$('body').append('<div id="temp-load"><div id="grid"></div></div>');
$('#temp-load > #grid').append(content)
$('#temp-load > #grid').children().css({
opacity: 0
});
var toAdd = $('#temp-load > #grid').html();
$container.isotope('insert', $(toAdd), function(){
$container.children().css({
opacity: 1
});
$('#temp-load').remove();
});
}
});
Make a single timeout to run the callback. This may avoid the function from executing multiple times.
var timer;
function scrollEvt() {
/* scroll actions */
}
$(window).scroll(function() {
/* clear the old timeout */
clearTimeout(timer);
/* wait until 400 ms for callback */
timer = setTimeout(scrollEvt, 400);
});
Using other ways may result in problems (like comparing (window.performance || Date).now())...
Unbind that specific scroll event till your delayed operation is completed to prevent accumulating more of the event triggers which create the duplication behaviour as in your case.
var winCached = $(window),
docCached = $(document)
var currLeng = 0;
function addContent() {
dettachScrollEvent();
setTimeout(function() { //this timeout simulates the delay from the ajax post
for (var i = currLeng; i < currLeng + 100; i++)
$('div').append(i + '<br />');
currLeng = i;
console.log("called loader!");
attachScrollEvent();
}, 500);
}
function infiNLoader() {
if (winCached.scrollTop() + winCached.height() > docCached.height() - 300) {
addContent();
//alert("near bottom! Adding more dummy content for infinite scrolling");
}
}
function attachScrollEvent() {
winCached.scroll(infiNLoader);
}
function dettachScrollEvent() {
winCached.unbind('scroll', infiNLoader);
}
addContent();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>

How to reinitialize HammerJS on resize event?

How do I reinitialize HammerJS on resize event?
My problem is when I trigger the resize event the Selectors reset, and using jQuery for matching a container $('.container') will return an index of 0 all the time.. on document ready it worked before.
The $('.container') appears more than once so it should increment on swipe event.
Anyone got any ideas ?
$(document).ready(function() {
mobileCalculus(animation);
swipeActions( $('.home-element-wrapper'), $('.home').width());
$(window).on('resize', function() {
mobileCalculus(animation);
});
});
function swipeActions(parent, offset) {
var curInd = 0;
var theThumb = $('.thumb');
var myElement = document.getElementById('main-swipe');
var max = theThumb.length - 1;
// create a simple instance
// by default, it only adds horizontal recognizers
var mc = new Hammer(myElement);
// listen to events...
mc.on("swipeleft", function(ev) {
curInd = $('.thumb.active').index() + 1;
if (curInd > max) {
curInd = max;
}
var newML = offset * curInd;
theThumb.removeClass('active');
theThumb.eq(curInd).addClass('active');
console.log($('.thumb.active').index() + 1);
parent.css({
'margin-left': '-'+newML+'px'
});
});
mc.on("swiperight", function(ev) {
curInd = $('.thumb.active').index() - 1;
if (curInd < 0) {
curInd = 0;
}
var newML = offset * curInd;
theThumb.removeClass('active');
theThumb.eq(curInd).addClass('active');
parent.css({
'margin-left': '-'+newML+'px'
});
});
}
For example jquery has a function called .off which deactivates the event after which we can put a new event on the selector.
$('element').off('swiperight').on('swiperight')
Apparently I've figured it out. I had to make the var mc = new Hammer(myElement); value "global". Apparently when I reinitialized it the second time in swipeActions(), the off() function would not work, so initializing the value only once and then performing off() and on() with it makes everything work.

Styling Elements that have been appended using jquery

I have div elements that are appended on a parent div. Depending on the screen size, the div elements should resize but my efforts to do so using jquery .css() are failing all through. Kindly help. Below is a sample of my code
var len = data.length; //a number comes here
var i;
for(i=1;i<len+1;i++){
$(".app-body").append(
"<div class='list-box' onclick='OpenHymn("+ i +")'>" +
"<div class='circle-text'><div class='hym_num'>" + i + "</div></div>" +
"</div>");
}
//calling the function that should resize the divs
GridBoxes();
function GridBoxes(){
var width=window.innerWidth;
var divisible=parseInt(width-40);
var size=divisible/4;
var boxes=$('.list-box');
boxes.css("width",size+"px");
boxes.css("height",size+"px");
}
Put the function inside $(window).resize() for it to fire everytime the window is resized
Code:
$(window).resize(function() {
GridBoxes();
})
Example
You should really use media query's for this kind of problem.
try this
window.addEventListener("resize", GridBoxes);//called on window resize
var len = 4; //changed to a number here for testing
var i;
for(i=1;i<len+1;i++){
$(".app-body").append(
"<div class='list-box' onclick='OpenHymn("+ i +")'>" +
"<div class='circle-text'><div class='hym_num'>" + i + "</div></div>" +
"</div>");
}
//calling the function that should resize the divs
//GridBoxes();
function GridBoxes(){
var width=window.innerWidth;
var divisible=parseInt(width-40);
var size=divisible/4;
var boxes=$('.list-box');
for(var b=0;b<boxes.length;b++){
boxes[b].css("width",size+"px");
boxes[b].css("height",size+"px");
}
}
.list-box{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="app-body"></div>

CSS not getting updated through jQuery

I am stuck in a silly problem. I have two divs (white coloured). Onclick them a class should be added which changes the color from white to blue. The class is getting added but the colour is not changing. (I do not need to addClass() just to change colour, but there will be more things in that class later, right now I have to execute it properly).
The divs has a class "thumbnail", onClick I want to add "border-glow".
// ImageBox component
// Author Sayantan Hore
// Created on 19.08.2014
// --------------------------------
function ImageBox(){
this.outerContainer = null;
this.imageBoxArray = [];
}
ImageBox.prototype.createOuterContainer = function(){
this.outerContainer = $("<div></div>")
this.outerContainer.addClass("outer-image-container");
$("body").append(this.outerContainer);
}
ImageBox.prototype.createImageBox = function(){
if (this.createOuterContainer === null){
this.createOuterContainer();
}
var imageBox = $("<div></div>");
imageBox.addClass("thumbnail");
var closeButton = $("<div>x</div>");
closeButton.addClass("btn-close");
//imageBox.append(closeButton);
this.imageBoxArray.push(imageBox);
return imageBox;
}
ImageBox.prototype.loadImage = function(imPath, imageBox){
var img = $("<img></img>")
img.attr("src", imPath);
imageBox.append(img);
img.load(function(){
//console.log($(this).height());
imgWidth = $(this).width();
$(this).parent().height(rowHeight);
$(this).height(rowHeight);
//console.log($(this).width());
$(this).parent().width($(this).width());
});
}
var rowHeight;
$(document).ready(function(){
rowHeight = parseFloat(($(window).height() * 90 / 100) / 5);
//console.log(rowHeight);
var imageBoxObj = new ImageBox();
imageBoxObj.createOuterContainer();
for (var i = 0; i < 2; i++){
var imageBox = imageBoxObj.createImageBox();
imageBoxObj.outerContainer.append(imageBox);
var imPath = '../images/im' + (i + 1) + '.jpg';
//imageBoxObj.loadImage(imPath, imageBox);
}
console.log(screen.availHeight);
console.log($(window).height());
console.log($(".outer-image-container").height());
$(".thumbnail").on("click", function(){
$(this).addClass("border-glow");
alert($(this).hasClass("border-glow"));
})
});
here is Fiddle:
see line numbers 57-60 in javascript part.
Can anybody help please?
.outer-image-container .thumbnail.border-glow{
background-color: #0066CC; }
This should make the trick, just replace your borderglow class with this.
the background-color in the class thumbnail is overriding the one in class `border-glow``because it gave more strong css selector. The simpler solution would be to edit the CSS like this :
.border-glow{
background-color: #0066CC !important;
}
Simply do the following
.border-glow{
background-color: #0066CC !important;
}
FIDDLE DEMO
add $(this).removeClass("thumbnail"); before
$(this).addClass("border-glow");

Text pagination inside a DIV with image

I want to paginate a text in some div so it will fit the allowed area
Logic is pretty simple:
1. split text into words
2. add word by word into and calculate element height
3. if we exceed the height - create next page
It works quite good
here is JS function i've used:
function paginate() {
var newPage = $('<pre class="text-page" />');
contentBox.empty().append(newPage);
var betterPageText='';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
} else {
betterPageText = betterPageText + ' ' + words[i];
}
newPage.text(betterPageText + ' ...');
if (newPage.height() >= wantedHeight) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
newPage.text(betterPageText);
newPage.clone().insertBefore(newPage)
betterPageText = '...';
isNewPage = true;
} else {
newPage.text(betterPageText);
}
}
contentBox.craftyslide({ height: wantedHeight });
}
But when i add an image it break everything. In this case text overflows 'green' area.
Working fiddle: http://jsfiddle.net/74W4N/7/
Is there a better way to paginate the text and calculate element height?
Except the fact that there are many more variables to calculate,not just only the word width & height, but also new lines,margins paddings and how each browser outputs everything.
Then by adding an image (almost impossible if the image is higher or larger as the max width or height) if it's smaller it also has margins/paddings. and it could start at the end of a line and so break up everything again.basically only on the first page you could add an image simply by calculating it's width+margin and height+margin/lineheight. but that needs alot math to get the wanted result.
Said that i tried some time ago to write a similar script but stopped cause of to many problems and different browser results.
Now reading your question i came across something that i read some time ago:
-webkit-column-count
so i made a different approach of your function that leaves out all this calculations.
don't judge the code as i wrote it just now.(i tested on chrome, other browsers need different prefixes.)
var div=document.getElementsByTagName('div')[0].firstChild,
maxWidth=300,
maxHeigth=200,
div.style.width=maxWidth+'px';
currentHeight=div.offsetHeight;
columns=Math.ceil(currentHeight/maxHeigth);
div.style['-webkit-column-count']=columns;
div.style.width=(maxWidth*columns)+'px';
div.style['-webkit-transition']='all 700ms ease';
div.style['-webkit-column-gap']='0px';
//if you change the column-gap you need to
//add padding before calculating the normal div.
//also the line height should be an integer that
// is divisible of the max height
here is an Example
http://jsfiddle.net/HNF3d/10/
adding an image smaller than the max height & width in the first page would not mess up everything.
and it looks like it's supported by all modern browsers now.(with the correct prefixes)
In my experience, trying to calculate and reposition text in HTML is almost an exercise in futility. There are too many variations among browsers, operating systems, and font issues.
My suggestion would be to take advantage of the overflow CSS property. This, combined with using em sizing for heights, should allow you to define a div block that only shows a defined number of lines (regardless of the size and type of the font). Combine this with a bit of javascript to scroll the containing div element, and you have pagination.
I've hacked together a quick proof of concept in JSFiddle, which you can see here: http://jsfiddle.net/8CMzY/1/
It's missing a previous button and a way of showing the number of pages, but these should be very simple additions.
EDIT: I originally linked to the wrong version for the JSFiddle concept
Solved by using jQuery.clone() method and performing all calculations on hidden copy of original HTML element
function paginate() {
var section = $('.section');
var cloneSection = section.clone().insertAfter(section).css({ position: 'absolute', left: -9999, width: section.width(), zIndex: -999 });
cloneSection.css({ width: section.width() });
var descBox = cloneSection.find('.holder-description').css({ height: 'auto' });
var newPage = $('<pre class="text-page" />');
contentBox.empty();
descBox.empty();
var betterPageText = '';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
var oldText = '';
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
descBox.empty();
}
betterPageText = betterPageText + ' ' + words[i];
oldText = betterPageText;
descBox.text(betterPageText + ' ...');
if (descBox.height() >= wantedHeight) {
if (i != words.length - 1) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
oldText += ' ... ';
}
newPage.text(oldText);
newPage.clone().appendTo(contentBox);
betterPageText = '... ';
isNewPage = true;
} else {
descBox.text(betterPageText);
if (i == words.length - 1) {
newPage.text(betterPageText).appendTo(contentBox);
}
}
}
if (pageNum > 0) {
contentBox.craftyslide({ height: wantedHeight });
}
cloneSection.remove();
}
live demo: http://jsfiddle.net/74W4N/19/
I actually came to an easier solution based on what #cocco has done, which also works in IE9.
For me it was important to keep the backward compatibility and the animation and so on was irrelevant so I stripped them down. You can see it here: http://jsfiddle.net/HNF3d/63/
heart of it is the fact that I dont limit height and present horizontal pagination as vertical.
var parentDiv = div = document.getElementsByTagName('div')[0];
var div = parentDiv.firstChild,
maxWidth = 300,
maxHeigth = 200,
t = function (e) {
div.style.webkitTransform = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
div.style["-ms-transform"] = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
};
div.style.width = maxWidth + 'px';
currentHeight = div.offsetHeight;
columns = Math.ceil(currentHeight / maxHeigth);
links = [];
while (columns--) {
links[columns] = '<span>' + (columns + 1) + '</span>';
}
var l = document.createElement('div');
l.innerHTML = links.join('');
l.onclick = t;
document.body.appendChild(l)

Categories