jQuery store .each() .text() for truncation possible? - javascript

I have asked a similar question previously, but didn't give enough context. As a result I received an excellent, technically-correct answer that didn't solve my issue.
I've also looked around on Stack but don't know enough about jQuery to find my answer.
I need to truncate multi-line text with jQuery. The code needs to add/remove text as well when the browser window expands and contracts. So from my minimal understanding the code needs to store the text before truncating it so that it can add text back in when the browser window is expanded.
Initially this piece of code solved my problem:
$(function () {
var initial = $('.js-text').text();
$('.js-text').text(initial);
while($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
$(window).resize(function() {
$('.js-text').text(initial);
while($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
});
});
This code no longer cuts it as when I use these .js classes more than once on a single page all the text is stored together and then spat out whenever the classes are being used.
Here is a jsFiddle of the the issue:
http://jsfiddle.net/1ddxtpke/
I need to store each .js-text text separately, so that I can use this jQuery snippet across a large project and have all instances of truncated text fed back into the DOM if a user were to expand their browser window size.
Is this possible? If so, how would I do it?
Thanks in advance for tackling my question. I hope I have been specific enough in what I'm looking for.

There are several ways how to do this. You can store it in an array:
var initialValues = [];
// Save the initial data
$('.js-text').each(function () {
initialValues.push($(this).text());
});
// On start
while($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
// When the window gets resized
$(window).resize(function() {
$('.js-text').text(function () { return initialValues[$('.js-text').index($(this))]; });
while($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
});
It has a catch though - the .js-text elements can't be erased or moved about, because it'll destroy the ordering. That'd require another function for order resetting in case something changes.
I haven't tested it, but in principle it should work this way.
EDIT: Okay, I reworked it a bit and here's the result:
var initialValues = [];
// Save the initial data
$('.js-text').each(function () {
initialValues.push($(this).text());
while ($(this).outerHeight() > $(this).parent().height()) {
$(this).text($(this).text().replace(/\W*\s(\S)*$/, '...'));
}
});
// When the window gets resized
$(window).resize(function() {
$('.js-text').each(function (index) {
$(this).text(initialValues[index]);
while ($(this).outerHeight() > $(this).parent().height()) {
$(this).text($(this).text().replace(/\W*\s(\S)*$/, '...'));
}
});
});

I see 2 ways of doing this :
1) Storing the full text as an attribute when needed. With this your text will stay with your div and can be retrived on expanding with a simple .attr .
2) Storing the text in an array and storing the index as an attribute on the div. This way is probably much more efficient than the previous one as I'm not sure what is the max length of a value of an attribute.

your function one syntax error
var initialValues = [];
// Save the initial data
$('.js-text').each(function () {
initialValues.push($(this).text());
});
// On start
while ($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function (index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
// When the window gets resized
$(window).resize(function() {
$('.js-text').text(function () { return initialValues[$('.js-text').index($(this))]; });
while($('.js-text').outerHeight() > $('.js-text-truncator').height()) {
$('.js-text').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
});
Demo Link http://jsfiddle.net/1ddxtpke/2/

Related

Adding comma to jQuery animated numbers

I'm using a jQuery script from here.
So far it works great, but the only thing that I'm wondering about is how to go about getting a comma into one of the values.
Since the number animate up when you scroll, it's tricky to get the browser to check what the integer is after the fact. I've read a bunch of posts that will add a comma (in this case I'm trying to get 100,000) but it won't work since the browser can't see that integer initially (starts at 0).
Here's the script that I'm using to invoke the animaition when you scroll down the page:
$(function () {
var fx = function fx() {
$(".stat-number").each(function (i, el) {
var data = parseInt(this.dataset.n, 10);
var props = {
"from": {
"count": 0
},
"to": {
"count": data
}
};
$(props.from).animate(props.to, {
duration: 500 * 1,
step: function (now, fx) {
$(el).text(Math.ceil(now));
},
complete:function() {
if (el.dataset.sym !== undefined) {
el.textContent = el.textContent.concat(el.dataset.sym)
}
}
});
});
};
var reset = function reset() {
console.log($(this).scrollTop())
if ($(this).scrollTop() > 950) {
$(this).off("scroll");
fx()
}
};
$(window).on("scroll", reset);
});
It takes the value from the data attribute data-n in the HTML and then works it's magic from there. If I have 100,000 in the data-n attribute then it cuts it short at 100 when the numbers animate.
So yeah, I'm not sure if I need to amend this script so it can accommodate for the comma in the data attribute or if there is something else I might need to do after the fact?
Ok, so after digging through it a bit more, I was able to come up with a solution.
I found this:
$.fn.digits = function(){
return this.each(function(){
$(this).text( $(this).text().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,") );
})
}
and then added an addtional class in my HTML.
I then hooked into the complete:function and added:
$(".class-i-added").digits();
to the end of the animation.
Overall, that appears to provide the result that I am looking for.
Since I only need to attribute it to one number (so far) it works as it should.

Jquery. How to get new parameters of elements after window resize using each?

Hello.
var repo = {
doSmth: function(){
$('.block').each(function(i, obj) {
console.log($(this).width());
})
}
}
(function() {
repo.doSmth();
function onResize(){
repo.doSmth();
};
$(window).on('resize',onResize);
})();
When I open the page, I see the real sizes of elements in the console, but when I change window size doSmth() is working, but it shows initial elements' size.
I need actual data after each refresh. How can I do this? I need a kind of live functionality on resize.
I could not get your code to run, so I wrote something else that should do what you need:
$(window).on('resize',onResize);
function onResize() {
var block = $('.block');
block.each(function (){
console.log($(this).width());
});
}
console.log($(this).width());
I thought the problem could be that the 'this' object had changed.

Hiding images with same data value jquery

Im trying to code a site where the objective is to click on two identical images and it hides the both the images you've managed to match to eachother.
$(document).ready(function(){
var animal1;
var animal2;
$(".memory1").on("click", function(){
animal1 = $(this).data('animal');
});
$(".memory2").on("click", function(){
animal2 = $(this).data('animal');
if (animal1==animal2){
$(this).data('animal').hide();
}
else {
alert("Wrong, Try again!");
}
});
});
so the line where its going wrong is obviously
$(this).data('animal').hide();
But I cant figure out a way to hide both images, or a better way of going about it.. :/
http://jsfiddle.net/4vgfca76/
This doesn't work the way you think it does
$(this).data('animal').hide();
When data is used with one argument, it get's the data attribute, which you should already know as you're doing it a few lines above.
What you get is the string hund etc. and that string doesn't have a hide() method.
You should be using the attributes selector to select the elements with that attribute instead
$(document).ready(function () {
var animal1, animal2;
$(".memory1").on("click", function () {
animal1 = $(this).data('animal');
});
$(".memory2").on("click", function () {
animal2 = $(this).data('animal');
if (animal1 == animal2) {
$('img[data-animal="'+animal1+'"]').hide();
} else {
alert("Fel! Försök igen");
}
});
});

Javascript ignoring if statements

I'm somewhat new to Javascript. I'm trying to make it so that clicking on an image on one page takes you to a new page and shows a specific div on that new page, so I used sessionStorage to remember and booleans to keep track of which image is being clicked. Right now, the code always executes the first if statement, regardless of which image is clicked. This code works fine in normal java so I can't figure out why my if statements are being ignored in javascript. I also tried adding an 'else' at the end, and tried ===. Here's my javscript, and thank you!
sessionStorage.clickedLeft;
sessionStorage.clickedMiddle;
sessionStorage.clickedRight;
function openedProjectFromGallery() {
if(sessionStorage.clickedLeft) {
$(".left-project-pop-up").show();
} else if (sessionStorage.clickedMiddle) {
$(".middle-project-pop-up").show();
} else if (sessionStorage.clickedRight) {
$(".right-project-pop-up").show();
}
sessionStorage.clickedLeft = false;
sessionStorage.clickedMiddle = false;
sessionStorage.clickedRight = false;
}
$("document").ready(function () {
$(".pop-up .x-button").click(function(){
$(".pop-up").hide();
});
$(".project-description .x-button").click(function(){
$(".project-pop-up").hide();
});
$(".left-project-thumb img").on("click", ".left-project-thumb img", function(){
sessionStorage.clickedLeft = true;
sessionStorage.clickedMiddle = false;
sessionStorage.clickedRight = false;
openedProjectFromGallery();
});
$(".profile-left-project img").click(function(){
$(".left-project-pop-up").show(1000);
});
$(".middle-project-thumb img").on("click", ".middle-project-thumb img", (function(){
sessionStorage.clickedMiddle = true;
sessionStorage.clickedLeft = false;
sessionStorage.clickedRight = false;
openedProjectFromGallery();
});
$(".profile-middle-project img").click(function(){
$(".middle-project-pop-up").show(1000);
});
$(".right-project-thumb img").on("click", ".right-project-thumb img", (function(){
sessionStorage.clickedRight = true;
sessionStorage.clickedLeft = false;
sessionStorage.clickedMiddle = false;
openedProjectFromGallery();
});
$(".profile-right-project img").click(function(){
$(".right-project-pop-up").show(1000);
});
});
You are defining function openedProjectFromGallery() with in document.ready . Define it outside document.ready and also give your three booleans some initial value at the top of your code if not initialized with some value or they are empty. I hope this would help.
It is not really answer to your orginal question,as the main issue with your code is, as #njzk2 says, that openProjectFromGallery only being called once, and not on each event, however I wanted to put my two coins on how this code could look like.
This is good example where custom events should be used
$(document).on('showPopup', function( e, popup ) {
$('.'+popup + '-project-pop-up').show()
})
$(document).on('hidePopup', function( e ) {
$('.popup').hide()
})
$('.left-project-thumb img').on('click', function(e) {
$(document).trigger('showPopup', ['left'])
})
$('.right-project-thumb img').on('click', function(e) {
$(document).trigger('showPopup', ['right'])
})
I think you get an idea.
On the other hand, it always nice to use event delegation with a lot of similar events as well as dom data.
<div class='popup' data-popup='left'>
<img />
</div>
$(document).on('click','.popup', function( e ) {
$(document).trigger('showPopup', [$(this).data('popup')])
})
From what I can see openedProjectFromGallery is only getting called on document load.
Add a call to it into each of the event handling functions or use jQuery's delegate function to assign event handling to each image.

Refactoring Code

Let's say I have the following code:
$(function () {
$(".buy-it-now.ribbon").click(function () {
$(".bid-to-beat.ribbon.active").removeClass("active");
$(".bid-to-beat.ribbon").addClass("inactive");
$(".buy-it-now.ribbon.inactive").removeClass("inactive");
$(".buy-it-now.ribbon").addClass("active");
$(".bid-now").hide();
$(".buy-now").show();
$(".add-to-cart").hide();
})
$(".bid-to-beat.ribbon").click(function () {
$(".buy-it-now.ribbon.active").removeClass("active");
$(".buy-it-now.ribbon").addClass("inactive");
$(".bid-to-beat.ribbon").removeClass("inactive");
$(".bid-to-beat.ribbon").addClass("active");
$(".buy-now").hide();
$(".bid-now").show();
$(".add-to-cart").show();
});
});
It is a simple function that allows for multiple UI related things to happen on the front-end of a site I am working on. I am fairly (very) new to jQuery and JavaScript in general and am learning about refactoring and making my code more condensed now. The way I currently write code is sort of line per thought I have. So my question is how would an experienced developer write this same code? Or rather, how could I refactor this code?
Try the following:
$(function () {
var $handlers = $('.buy-it-now.ribbon, .bid-to-beat.ribbon');
$handlers.click(function() {
$handlers.toggleClass("active inactive");
var $elements = $(".bid-now, .add-to-cart"),
$buyElement = $(".buy-now");
if($(this).is('.buy-it-now.ribbon')) {
$elements.hide();
$buyElement.show();
} else {
$elements.show();
$buyElement.hide();
}
});
});
This question would be better suited for codereview, but yes it can be condensed a little using method chaining.
$(function () {
$(".buy-it-now.ribbon").click(function () {
$(".bid-to-beat.ribbon").removeClass("active").addClass("inactive");
$(".buy-it-now.ribbon").removeClass("inactive").addClass("active");
$(".bid-now").hide();
$(".buy-now").show();
$(".add-to-cart").hide();
})
$(".bid-to-beat.ribbon").click(function () {
$(".buy-it-now.ribbon").removeClass("active").addClass("inactive");
$(".bid-to-beat.ribbon").removeClass("inactive").addClass("active");
$(".buy-now").hide();
$(".bid-now").show();
$(".add-to-cart").show();
});
});
You could condense it further by pre selecting the elements and caching them in variables before the click events as long as no elements are added or removed during the life of the page.
As your code it is you can combine some of the selectors into a single line. And also because your elements looks to be static you can cache them into a variable and use them later as it reduces the number of times a element is looked up in the DOM reducing the accessing time..
Also you can limit the scope of these variables or selectors by encasing them in an object or a closure..
Maybe something in these lines..
$(function () {
cart.init();
});
var cart = {
elems : {
$buyRibbon : null,
$bidRibbon : null,
$bidNow: null,
$buyNow: null,
$addToCart: null
},
events : {
},
init : function() {
this.elems.$buyRibbon = $(".buy-it-now.ribbon");
this.elems.$bidRibbon = $(".bid-to-beat.ribbon");
this.elems.$bidNow = $(".bid-now") ;
this.elems.$buyNow = $(".buy-now") ;
this.elems.$addToCart = $(".add-to-cart") ;
this.events.buyClick();
this.events.bidClick();
}
};
cart.events.buyClick = function() {
cart.elems.$buyRibbon.on('click', function(){
cart.elems.$bidRibbon.removeClass('active').addClass('inactive');
cart.elems.$buyRibbon.removeClass('inactive').addClass('active');
cart.elems.$bidNow.hide();
cart.elems.$buyNow.show();
cart.elems.$addToCart.hide();
});
}
cart.events.bidClick = function() {
cart.elems.$bidRibbon.on('click', function(){
cart.elems.$buyRibbon.removeClass('active').addClass('inactive');
cart.elems.$bidRibbon.removeClass('inactive').addClass('active');
cart.elems.$bidNow.show();
cart.elems.$buyNow.hide();
cart.elems.$addToCart.show();
});
}
So basically in here your whole cart is a object ..And the cart has different properties which are related to this.. You follow the principles of object oriented programming here..
Using closures I heard gives you better design limiting the scope of your code..
Might I suggest something like this:
$(function () {
var buyNowButton = $('buy-it-now.ribbon'),
bidToBeatButton = $('.bid-to-beat.ribbon'),
buyNowEls = $('.buy-now'),
bidToBeatEls = $('.bid-now,.add-to-cart');
var toggleButtons = function(showBuyNow){
buyNowButton.toggleClass('active', showBuyNow);
bidToBeatButton.toggleClass('active', !showBuyNow);
buyNowEls.toggle(showBuyNow);
bidToBeatEls.toggle(!showBuyNow);
}
buyNowButton.click(function(){ toggleButtons(true) });
bidToBeatButton.click(function(){ toggleButtons(false) });
});
You could save a some lines by removing the selectors at the start and just do the selection in place, if the saved space would be more important than the minor performance hit. Then it would look like this:
$(function () {
var toggleButtons = function(showBuyNow){
$('buy-it-now.ribbon').toggleClass('active', showBuyNow);
$('.bid-to-beat.ribbon').toggleClass('active', !showBuyNow);
$('.buy-now').toggle(showBuyNow);
$('.bid-now,.add-to-cart').toggle(!showBuyNow);
}
$('buy-it-now.ribbon').click(function(){ toggleButtons(true) });
$('.bid-to-beat.ribbon').click(function(){ toggleButtons(false) });
});
The first version selects the elements once and holds them in memory; the second selects them each time the button is clicked. Both solve the problem I believe would occur with the selected answer where clicking the same button twice would cause the .active and .inactive classes to get out of sync with the shown/hidden elements.

Categories