javascript, change class but only when it meets certain criteria - javascript

I have a class that is used ~~120 times on a page. I would like to change the margin-bottom only if other things affecting that class meet a certain criteria. The problem with the below code is that it changes everything associated with that class...not just the ones that meet the criteria.
if (actualCharacters > charactersCanFitInContainer) {
$("#Full .t16").each(function () {
$(this).css('margin-bottom', '1.25em');
});
$("#Full .t8").each(function () {
$(this).css('margin-bottom', '4.4175em');
});
}

I want to help you with your problem, but the premise is wrong. You shouldn't be trying to do this in jQuery. Maybe there is something wrong with your CSS, and it would be better to try to resolve it using CSS.
Having said that let's go over some of the problems with your code:
(1) Don't use $( selector ).each()
When you do a call to jQuery
$( selector );
This little guy returns an array of all elements that match the css of the selector.
There may be 200 elements, or even a thousand, but never, ever, do an .each() call unless you tend to explicitly change every individual element in a unique way.
$( selector ).each() runs a for loop on the array of matched selectors, which will give you performance problems if your matched set is too large.
To change all matched elements you need only to do this:
$( selector ).css('margin-bottom', '1.25em');
Read more about .each()
(2) Do not use Javascript (or jQuery), to do CSS's job
From your question it seems like you're having a problem with spacing. CSS has a number of ways to resolve this using rules like overflow, white-space, text-overflow that you should explore before resorting to scripting.
(3) Avoid using .css()
You should avoid using the $( selector ).css() function since it also introduces performance problems, especially on large sets. Instead, you should create a class that you can apply to the set and use:
$( selector ).toggleClass( 'myclass' );
//or
$( selector ).addClass( 'myclass' ); //and
$( selector ).removeClass( 'myclass' );
since these functions are more performant.
To take it a step further, do not apply a class to every set of matching elements, rather, add the class to a parent element and let the children inherit their styles from the updated parent class.
Read more about toggleClass()
(5) Stick to conventions
While it's perfectly OK to use capital letters in naming your CSS rules, you should probably avoid that since it's not standard practice. The whole point of having a standard practice is so that everyone can look at your code and know exactly what is going on, which makes your code easier to debug and share.
(6) Don't try to compensate for bad design with over-engineered solutions
Also consider that, sometimes, the requirements need to be changed if your only solution is to script the styles.
I've run into situations where what a project manager wanted to accomplish was not technically feasible. If they want a large body of text to display in a limited area, they need to allow for things like scrollbars or consider keeping a standard size limitation on blocks of text.
Go back to the stakeholder for this project and tell them that what they want to do is not reasonable, and make decisions together to design this widget better.
As a developer, you should not allow for unreasonable requirements, some things simply need to be redesigned instead of making you come up with a messy way to resolve bad design.
Conclusion
In terms of your problem, play around with the CSS rules that specifically address the spacing problem you're having (overflow, text-overflow, etc). You may need to look deeper into the styles to find a better way to do this.

In case anyone stumbles across this question and answer, I wanted to post what I ended up doing - special thanks to Qodeninja for setting me straight.
Rather than change CSS via javascript, I went with simple #media queries to handle the various font and spacing changes below. There are a variety of differing methods to do responsive design. This worked best for my requirements
#media all and (max-width: 1500px)
{
body
{
font-size:1.1em;
}
#Full
{
width:1200px;
background-color:Blue;
}
.theTeams
{
width:8.925em;
padding-left:.238em;
padding-right:.238em;
}
.t8{margin-bottom:4.75em;}
.t4{margin-bottom:11.75em;}
.t2{margin-bottom:26.5em;}
.tend{margin-bottom:0em;}
.rtR{margin-left:19em;}
#media all and (max-width: 1200px)
{
body
{
font-size:0.9em;
}
#Full
{
width:900px;
background-color:Orange;
}
.theTeams
{
width:7.75em;
padding-left:.14em;
padding-right:.14em;
height:2.1em;
}
.t8{margin-bottom:4.95em;}
.t4{margin-bottom:12.25em;}
.t2{margin-bottom:27.75em;}
.tend{margin-bottom:0em;}
.rtL{margin-left:16em;}
}

Related

Can I use javascript to place one element on different places, one for mobile and one for desktop?

I am adding an element in the dom using javascript. I have added an using insertBefore() to place it where I want it on the mobile view. But in desktop it is supposed to be placed on a different space on the website. How can i solve this?
Just using CSS is not an option due to already existing elements that i cant't move.
var priceWrapper = document.querySelector('.price-info-wrap')
var mainContainer = document.querySelector('.price-info')
var addUrgency = document.getElementById('urgency')
priceWrapper.insertBefore(addUrgency, mainContainer)
The code provided is how I have placed "addUrgency" witch is the div I need to put elsewhere on desktop.
You can do it, but it's a bad idea.
Lay out your elements starting with smallest screen width you need, then work outwards from there using CSS Media queries to adjust the layout at specific screen widths as and when you need to.
In this case, if you can't do it any other way you could have both elements where you like them and then show/hide depending on the viewport width. Something like:
#media (min-width:800px) {
//your non-mobile styles and classes go here
.desktop-element{
display: inline-block;
}
.mobile-element{
display:none;
}
}
You could use navigator.userAgent and determine if the browser is a mobile browser. There is also an question with really good answeres on doing that on StackOverflow: Detecting a mobile browser
Another option is to check the viewport-size with javascript. Which can be a better solution in the case you have css-rules in place that are responsive to the viewport-size , like: #media (width):
let width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
After that it is a simple if-else to decide where to place your element.
You can use the onresize event. But, I have to note, that having two identical elements (perhaps using clone() to copy #addUrgency) in the DOM on their right places and display/not display them using CSS media queries instead of using Javascript to re-lay elements every time the viewport is resized is a better solution. However, to answer your question here is the most straightforward approach using your code. It also worth to note, that resize event can fire pretty fast, so you probably will want to throttle the function relay (answers are on StackOverflow).
function relay(){
var addUrgency = document.getElementById('urgency');
if(`mobile view`){ //here goes a condition to determine what view you have.
var priceWrapper = document.querySelector('.price-info-wrap');
var mainContainer = document.querySelector('.price-info');
priceWrapper.insertBefore(addUrgency, mainContainer);
}else{
// Insert where you want it on desktop view
}
}
window.onload = function() {
relay();
document.body.addEventListener("resize", relay);
};
You could do this using JavaScript but I wouldn't recommend this approach as I believe this is achievable in most cases with plain HTML and CSS. One method would be using display: grid and placing the elements in the desired rows/columns on mobile (If you are of course utilizing a mobile-first approach) and then redistributing them on larger screens with media queries. Additionally, you could of course make use of position: absolute whenever this strategy does not completely do the job.
Here is a great article, in case you are not completely familiar with display: grid.

Applying state changes that apply to many elements in a block

I like BEM a lot, and I usually use a variant of BEM where I use state classes to toggle sub-items on/off, leading to easy to understand rules in my SASS like:
.my-block{
&__element {
color : blue;
// I prefer state-classes over modifiers for state
&.is-selected { color : red; }
&--small { height: 50%; }
}
}
The problem I wonder how to solve in the most BEM-ish way is how to deal with a state change in my javascript that should apply to a lot of elements in a block. For instance, say I have a component that should hide or show different elements depending on whether you are in step1, step2 or step3 of a process.
The BEM bits are easy enough if I should just apply state classes on each of the elements, but that is just the problem. If I have 10 elements where 7 needs to be hidden on step 2 then that is 7x as much javascript to add as if I had just added a rules on the not so BEM-pure form of
&__element {
display: none;
// -- this --
.my-block.is-step4 & { display : block }
}
I could then activate all of those rules with one myBlock.classList.toggle("is-step4"), as opposed to one for each element.
My solution was a pragmatic middle point of developer convenience and pure BEM, but I wonder if there is a "pure" BEM solution that is also friendly in terms of line of codes needed in the javascript to update the elements?
Just use nested selectors for this case. So you'll have step modifiers on the parent block which contains all the rest of blocks you need to affect (and don't be afraid if such parent is the whole page).
See https://en.bem.info/methodology/faq/#can-i-use-nested-selectors — it's recommended solution.

Performance css rules vs. jQuery .toggleClass

I'm running a wordpress theme, which has a sticky header. I've built a subnav, which is displayed right below the header. Whenever I use this subnav, the header should not stay sticky, instead the subnav should be sticky (already made with simple javascript addClass). I am only interested how to turn off the stickiness of the header the best way.
(1) Using CSS:
One way is to use css and to override the classes, which make the header sticky. This already works, I've just changed the position fixed to static and disabled the placeholder, which avoids a gap in the content, when the position is set to fixed and the header is taken out of the flow. Based on the circumstance that I want to use the subnav on many pages (ca. 70%) I would have to write one big css rule for targeting the relevant pages by their ids. This leads to my thought that javascript may better on resources.
(2) Using Javascript:
I've wrote a small snippet of javascript, which easily removes the whole css-classes, when the ID of the subnav is present on the page:
if(document.getElementById('subnavfullwidth')){
jQuery( '.header-sticky-height' ).toggleClass( 'header-sticky-height', 'false' );
jQuery( '.header-wrapper' ).toggleClass( 'header-is-sticky', 'false' );
jQuery( '.header' ).toggleClass( '.header-sticky-shadow', 'false' );
}
Simple as the title: Would the javascript be better or one big css-rule for the site performance? Is there a way to optimize my javascript or is it just okay?
Kind Regards!
I'd be more inclined to have one class added to '.header' (assuming that's the root element of the relevant area) named sticky, and change the class header to an id of header.
The way you have it written, you could gain a little performance by adding an id to '.header', and then chain methods together to limit the number of elements to have to search through.
$('#header')
.toggleClass('header-sticky-shadow', false)
.find('.header-wrapper')
.toggleClass('header-is-sticky', false)
.end()
.find('.header-sticky-height', false)
.toggleClass('header-sticky-height', false);

jQuery Add & Remove Class Div Width

How can i add/remove class according as div width ? I tried some codes but I have no idea about jquery codes. I'd like add div class according as antoher div width. Just i need add class like that. If container is smaller than 600px "add class" to content div else "remove class" from content div. These are my codes;
<div class="container">
<div class="content"></div>
</div>
$(window).resizeboxes(function() {
if ($(".container").width < 600){
$( ".content" ).addClass( ".content_600" );
}
});
else{
removeClass('.content_600')
}
$(window).trigger('resizeboxes');
This works, though the code is changed slightly. There were some problems with the syntax also, so I've corrected those (for instance the else statement was slightly misplaced). Here is a working example:
https://jsfiddle.net/vt0nbx36/3/
Here is the code:
var resizeboxes = function() {
if ($(".container").width() < 600)
{
$(".content").addClass("content_600");
}
else
{
$(".content").removeClass("content_600")
}
};
resizeboxes();
$(window).resize(function(){
resizeboxes();
});
For this need exactly, you have jQuery's .toggleClass() function. It takes the class name as a first parameter, and optional second boolean parameter that states wether the class name should be added or removed. You can find the documentation here
$(".content").toggleClass("content_600", ($(".container").width() < 600));
Even tho your question is a JS related question, CSS as a matter of fact can handle this like no other beast can (mostly)!
CSS allows you to use media-queries to resize your content based on the width of the viewport.
The upside of this is that the browser will handle this for you within the rendering engine rather than having JS between your change and the rendering engine.
The major downside is that you can't define the width of element A based on element B but are unfortunately locked to using the viewport as an indicator.
Before I explain why you'd want to use CSS I'd like to point out why you don't want to use JS for this if possible.
The jQuery.resize eventhandler fires inconsistently across browsers and it fires alot of times usually.
This causes your scrolling to clog up and make your webpage feel laggy.
If there's anything your users will dislike it's the fact that scrolling is controlled by something they don't even know of which is slowing you down.
As for a CSS solution, a media query looks like this:
.my-selector {
width: 900px;
}
#media all and (max-width: 600px) {
.my-selector {
width: 600px;
...
}
}
You wrap your code in a sort-of conditional that allows you to be very flexible with manipulating elements on the page.
What happens in the above piece of code is that when the parser reads the CSS it sees the first selector not in a media query so it applies width: 900px; then it sees a media query and sees the other rule for my-selector however it will only apply that rule when the screen is at that width we defined in the #media ... rule. When you resize CSS handles things differently behind the scenes so it's much faster than JS in that case.
I'm not sure if it actually applies to your situation but if your container is sized by the viewport rather than parent elements this should be possible and I thought it'd be nice atleast to show you a good way of playing with element dimensions.
Also, you can use #media to for instance make a webpage print friendly by changing the all to print for example and setting the background-color: transparent for an element - saves ink ^.^ which is an additional extra on top of the general awesomeness of media queries.
Hope it helps, good luck if you wish to make your webpage 5 times faster ;)

Moving an element into a new div - doesn't take on its new css properties

So, I have an element that has some "pre-existing" behavior attached to it. So, I found that just moving it (as required by some new requirements) retains the existing behaviors = good. But the issue is, when I move the element I need to give it "new styles".
So, if I have something like this:
<div id="existingStructure">
<div id="legacyElement"></div>
</div>
Now, that has pre-existing styles attached to both. I can rearrange these styles etc.. but I can't change them. The styles are attached to the "id's" rather than a class definition. I believe I can change that if needed.
Now, I need to move "legacyElement" when certain things happen to a "new div".
I just
jQuery('#newStructure').append('#legacyElement');
<div id="newStructure">
<div id="legacyElement"></div>
</div>
Unfortunetly, the styles I have on newStructure don't seem to be applying to *legacyElement" when it gets moved here dynamically.
I was thinking of moving all the styles to a class rather than associated to the ids, and when I move it.. I just jQuery().addClass / jQuery().removeClass etc...
Is there a better/easier more robust way that I can just have the legacyElement loose its styles when it sits under existingStructure and then get new ones when moved to "newStructure" etc.. and vice versa. That element "legacyElement" will be pinging back and forth.. so, I need it to have the styles under each parent div as it goes there.
so when an action happens on page, I move it back:
jQuery('#existingStructure').append('#legacyElement');
If I am not succinct enough, please let me know.
The EXISTING styles are in an external CSS file and are like so..
#existingStructure {
// bunch of css
}
#existingStructure .item1 input[type="text"] {
// bunch of css
}
#legacyElement{
// bunch of css
}
and new styles are sorta the same except 'additional styles' might be applied.
#newStructure {
// bunch of css
}
#newStructure .item1 input[type="text"] {
// bunch of css
}
You can certainly target your div styles by their parents:
#existingStructure #legacyElement {some styles}
#newStructure #legacyElement {some other styles}
To explain futher, this arrangement should result in greater specificity, overriding styles that are simply applied to either #existingStructure or #legacyElement. I'm hoping no one did anything foolish like using !important on them.
Short answer: It should.
Here's an example I quickly made in jsFiddle: http://jsfiddle.net/CCm4J/1/
So then why isn't yours? Most likely you have css rules that are embedded that apply only when in the existingStructure id/class perhaps? Without see more of your css I'm not sure how specific I can get. I would just verify that your css rules are allowed to apply outside of existingStructure (and even that existingStructure might have rules for its parent too!)

Categories