How to make BEM less obsuse - javascript

I’ve started using BEM on some projects and find it a little unwieldy to write within my HTML. I was trying to make it simple so I have to repeat myself less when I’m writing classes for each element and apply some structure with JS that would force me to do this properly. The output would be something like:
<div class="intro intro--red">
<h1 class="intro__title">Title</h1>
<p class="intro__p">Text</p>
<p class="intro__p intro__p--red">Text</p>
Link
</div>
but written shorter and simpler like:
<div b="intro" m="red">
<h1 e="title">Title</h1>
<p>Text</p>
<p m="red">Text</p>
Link
</div>
This way I could have named elements or it can give me classes based on the html element if none are set. This would then hopefully help keep my sass nice and clean and be really quick to type. I attempted a basic implementation but I think there may be a better way to do this or I might be doing something I shouldn't using html attributes like that?
Any wisdom or pointers would be much appreciated?

I had this exact same issue using BEM. My solution was to make my code a form of BEM but not 100% according BEM guidelines.
What i did is something like this:
<div class="intro">
<div class="intro-title">
<p class="red">Some Text</p>
</div>
</div>
So things like colors would get global helpers, then a bunch of helpers for simple styling like font sizes or spacing etc.
For the actual intro block this is how my sass would look like:
Intro.scss
.intro {
&-title {
float: left;
}
}
Helpers.scss
.red {
color: red;
}
As you can see you can go as deep with the nesting of sass as you want, i keep the rule of max 3 levels deep.
It's not a 100% BEM approach but it's a form off :)
Hope that helps you.
I would also avoid putting these custom attributes on html and parsing things with JS, when it comes to these things me personally I'm a bit of a purist and HTML is not meant for this so i wouldn't use it. Plus potential content flickers when loading the css through JS and keeping performance in mind as well. But it's totally your own preference, just giving you a pointer in how i solved this issue that works for me.

Related

JS closest/parent not working on loaded page

I'm working with a third-party code and I'm quite limited in terms of filtering a list of elements.
Each of these elements has this structure:
<div class="item-preview">
<div class="item-info">
<div class="tag">
<svg class="tag-public"></svg>
</div>
</div>
</div>
The only thing that changes is the svg class, so it's whether tag-public or tag-private. Depending on the user type that's checking the content, I'd like to hide it when it's tag-private. I've tried this:
$('.tag-private').closest('.item-preview').hide();
And this:
$('.tag-private').parents('.item-preview').hide();
But any of them works. The code uses React and the items are brought by JSON/AJAX, so I guess the problem is related to trying to modify the page once is loaded...
Any thoughts on how to make my JS "override" the original code? Thanks a ton.

Hover with Javascript (namespace)

With the use of namespace, i'm trying to make a div-element in Javascript where if you hover over the div it'll change color and change back when not hovering over it. Please help!
Is this what you need? There is many ways to do it, I used plain js. The css you put whetever you want.
I don't understand what the namespace means in your situation.
Following are the respective javascript, css and html. But things like this you can google and find the parts that you need.
function changeColors(){
if(document.getElementById("myElement").classList.contains('class1')){
document.getElementById("myElement").classList.remove('class1');
document.getElementById("myElement").classList.add('class2');
}else{
document.getElementById("myElement").classList.remove('class2');
document.getElementById("myElement").classList.add('class1');
}
}
.class1{
color:red;
}
.class2{
color:blue;
}
<div>
<h3 id="myElement" class="class1" onmouseover="changeColors()"
onmouseout="changeColors()">
Hello
</h3>
</div>

When is transclusion appropriate?

Case in point, I am developing a multi-select control in Angular2, similar in functionality to Select2 or a multitude of other controls.
I started by defining what I want the user interface to look like in terms of defining what's included in the dropdown, and came up with two options.
One is to use #Input()s for the options:
<my-multi-select [options]="options"></my-multi-select>
...and then within the template for my-multi-select:
<div class=option" *ngFor="let option of options">{{option.display}}</div>
Ahother is to use transclusion, which is how material2 appears to do it:
<my-multi-select>
<my-option *ngFor="let option of options" [value]="option"></my-option>
</my-multi-select>
...and then within the template for my-multi-select:
<div class=select-container">
<ng-content select="my-option"></ng-content>
</div>
I was content with the transclusion option, but then when I started to actually implement it rand in to difficulty binding the events coming from my-option to my-multi-select. I could try to figure out a way to notify my-select of things that are happening in my-option, like using an Observable, or digging deeper in to using an #Output event -- but that feels like trying to jam a square peg in to a round hole when #Input variables might just be simpler.
This led me to the question, is transclusion even appropriate here? And the bigger question, when is transclusion appropriate, and when is using transclusion jamming a square peg in to a round hole?
For your example, it's simple to compare the two aproaches as you are only including one text item as part of the transclusion. This makes using #Input() trivial and may well be the best solution.
However, imagine a scenario where you have multiple elements you want to include in your child component, each with custom HTML tags along for the ride. Using trasnclusion this is trivial, but using #Input() it requires a few "hacks" to get right and isn't very maintainable or extendable.
Explanation
Building on from this Todd Motto blog about transclusion as a reference, we can utilise transclusion to have more complex HTML for our title and content sections without issue.
Transculsion Parent
<my-component>
<my-title>
<h1>This is the Component title!</h1>
</my-title>
<my-content>
And here's some awesome content.
<ul>
<li>First</li>
<li>Second</li>
</ul>
</my-content>
</my-component>
Transclusion Child
<div class="my-component">
<div>
Title:
<ng-content select="my-title"></ng-content>
</div>
<div>
Content:
<ng-content select="my-content"></ng-content>
</div>
</div>
Now imagine this same scenario using only #Input() to declare our elements.
Our binding from the parent isn't very friendly, we definitely don't want to do this for more complex scenarios.
In our child component we have to use [innerHTML] to get around interpolation encoding in Angular. Again, this isn't very easy to extend or maintain. This is where in my opinion transclusion really excels.
Input Parent
<my-component
[my-title]="<h1>This is the Component title!</h1>"
[my-content]="And here's some awesome content.<ul><li>First</li><li>Second</li></ul>">
</my-component>
Input Child
<div class="my-component">
<div [innerhtml]="'Title:' + my-title"></div>
<div [innerhtml]="'Content:' + my-content"></div>
</div>
This led me to the question, is transclusion even appropriate here?
If want html to look like:
<my-multi-select>
<my-option *ngFor="let option of options" [value]="option"></my-option>
</my-multi-select>
Where my-multi-select and my-option are components, then transclusion is the way to do it.

Is there a way to add an element to the same height as another selected element dynamically?

I am new to front-end development. I was trying to code an annotation tool. A sample screen is shown on the image below. After the user select a sentence, an annotation box appears on the right side bar at the same horizontal position as the highlighted sentence. Any ideas about how I can achieve that effect?
Here is my html structure. I used the framework of Zurb Foundation:
<section id="main">
<div class="row">
<div class="small-8 large-8 columns"id="rawdata">
<p> <span class="sentence">2:22 So, last time I was here, I don't know if I told you this, but, um, we kind of did a "I like, I wish" activity on paper, about things that you like about studio, and things that you wish would change.</span><span class="sentence"> Um, do you want to share any of those thoughts now, so maybe we can talk about them? [name], I have yours if you want to look at it again.</span></p>
<p><span class="sentence">2:47 I forgot to add something.</span></p>
<p><span class="sentence">2:54 Well, I don't know, in terms of what I dislike about studio.</span></p>
<p><span class="sentence">2:57 So, some people wrote in theirs that, um, they dislike how cluttered it gets.</span></p>
<p><span class="sentence">5:09 I don't get bothered.</span>< <span class="sentence">I like the draftiness, I'm a little...</span><span class="sentence"> I'm one of the ones that opens the windows, and like—</span></p>
</div>
<div class="small-4 large-4 columns" id="annotations"><p></p>
</div>
</div>
</section>
JS for selecting sentence and adding annotations:
<script>
$('.sentence').click(function() {
$(this).toggleClass('sentenceStyle');
var y = $(this).offset().top;
var para = document.createElement("p");
$("#annotations").append(para);
para.innerHTML="this is an annotation";
para.css("position",'absolute');
para.style.top = y;
});
</script>
And here it is the fiddle: http://jsfiddle.net/yujuns/HDe6v/3/
There are some things that you want to change in your code.
First what you want is to get the offset of the selection. That can only happen if you put an html tag around the selection and then get its offset. You can then place an absolute positioned message box by setting its left and top offset to the offset you got from html element.
In the following fiddle, I have shown a basic implementation to give you the basic idea. Hope it helps.
Fiddle
EDIT:
Try this fiddle update.(In response to author's question). I have added comments to lines of code that I added to js. I also added position: relative to css for annotations
Updated Fiddle

Javascript output won't accept any CSS styling

I'm using simplecart.js which generates data for me to add to a cart, and then passes it to PayPal for me.
I have successfully added the 'add to basket' and 'checkout' features but am finding styling the JS-generated code impossible as no styles applied to it will work.
This is the code site has given to me, which generates a number of items such as name, quantity etc from stored data. It outputs all information correctly but any styles applied to the class names do nothing.
This is the code that generates the data:
<div class="simpleCart_items"></div>
This is the result from the web browser:
<div class="simpleCart_items"><div>
<div class="headerRow">
<div class="item-name">Name</div>
<div class="item-price">Price</div>
<div class="item-quantity">Qty</div>
<div class="item-remove"></div>
</div>
<div class="itemRow row-0 odd" id="cartItem_SCI-3">
<div class="item-name">The Jenny Snood £11</div>
<div class="item-price">£11.00</div>
<div class="item-quantity">1</div>
<div class="item-remove">
Remove
</div>
</div>
</div>
</div>
The browser is receiving all the data correctly, but applying any styles to the class names does nothing. For example, I have:
.headerRow{
background-colour:#0F0;
}
The result should be that the background of headerRow be lime, but nothing happens. It is not calling the style correctly.
I have tried everything but none of the classes will fetch the styles applied to them.
Here is a screenshot of how it looks, obviously not very nice unstyled, but I can't apply any styles at all to it.
Here is a link to the live site
A further examples:
I've added the code given which generates the totals:
<div class="simpleCart_total"></div>
I have tried giving it it's own new class and also styling the original, with !important - none of this works.
<div class="totals simpleCart_total"></div>
.simpleCart_total{
background-color:#0F0 !important;
}
.totals{
background-color:#0F0 !important;
}
None of the above seems to have any impact whatsoever.
There is some other css or inline javascript affecting the custom style your are attempting to use.
To mitigate this issue do one of the following:
Add !important after each css statement like so:
.headerRow{background-color:#0F0 !important;}
Reorder the css files so your custom css file is at the bottom
The problem here is that the styles are being applied dynamically by the js after your CSS (ie during the initialization of the plugin). You're only hope is to style it after this initialization with your own js, I think.
You can check this in Firebug... If you look at the elements there, you should see a bunch of inline styles being applied to them. These will trump your CSS in the header (and even the inline CSS you provide beforehand).
Edit: I saw some junky characters just before the directives in question (as well as those pointed out in another answer). I would try removing all the white space after the preceding directive and before the broken ones. It does not appear that the js is dynamically changing anything.

Categories