Progressive enhancement and "flash of unhidden content"? - javascript

I am trying to build a progressively enhanced page that works for JS and non-JS users. I would like to hide some form controls initially for JS users, but always show them for non-JS users.
My question is about how to do this without creating a distracting "visible, then instantly hidden" flash of content for JS users.
For example, in the JS version, I want to collapse part of my search form, and instead show a 'click here for extra options' button. I do this as follows:
$(document).ready(function() {
$("#extra-options").hide();
...
$("#show-extra-options").click(function() {
$("#extra-options").slideToggle();
});
});
This works, but it means that for JS users, when the page loads, the extra options are visible for 500ms or so, then they vanish. It's rather distracting.
Is there any sensible way to get around this?
StackOverflow has just suggested this answer: is this sensible? Sorry if this is now a duplicate question, but I figure it's still worth writing this question in my own language, as I didn't find the answer during searching.

Add this in your script tag in the head:
$('html').addClass('js');
Then you can use that to show and hide elements:
.hasJs { display: none; }
.js .hasJs { display: block; }
.js .noJs { display: none; }
You can hide content for either users with or without Javascript:
<div class="hasJs">Some content only visible for JS users.</div>
<div class="noJs">Some content only visible for non-JS users.</div>
As the class and CSS are in the head, the elements will already be styled when they come into existance when the body is parsed.
Demo: http://jsfiddle.net/Guffa/YuAyr/
This is a similar approach to the one in the first answer to the question that you linked to, but this is somewhat cleaner because you don't have to add a class to the html element in the markup, and the code simply adds the class instead of removing it.

Related

Updating a simple jQuery / CSS code

I was hoping someone could help me out with this simple question: I’ve just started to learn jQuery and found a code to show hidden text after selecting an item.
I’d like to update it so that:
a.) The selected item is bold
b.) I can add placeholder text instead of starting off with a blank hidden text field
I foolishly assumed I could solve a.) by using the :active property in css, but that only works as long as the link is clicked on. (As soon as you release the mouse button it’s gone.) Just like b.), this is probably only possible by using jQuery as well? If so, would be really great if you could show me how to solve it. :)
The codes: http://jsfiddle.net/KUtY5/1/
JS
$(document).ready(function(){
$("#nav a").click(function(){
var id = $(this).attr('id');
id = id.split('_');
$("#menu_container div").hide();
$("#menu_container #menu_"+id[1]).show();
});
});
CSS
#menu_container {
width: 650px;
height: auto;
padding-left: 30px;
}
#menu_container div {
display:none;
}
HTML
<div id='nav'>
<a id="show_apps">Appetizers</a> | <a id="show_soups">Soups and Salads</a> | <a id="show_entrees">Entrees</a>
</div>
<div id="menu_container">
<div id="menu_apps">
Content of the App Section Here
</div>
<div id="menu_soups">
Content of the Soups Section Here
</div>
<div id="menu_entrees">
Content of the Entrees Section Here
</div>
</div>
Updated fiddle
You can realize a) using a custom class bold for example and the following code :
CSS
.bold{ font-weight: bold;}
JS
$(this).addClass('bold').siblings('a').removeClass('bold');
For b) I can't find any textfield in your code.
Hope this helps.
I have added some extra lines to your code and you can check it from here http://jsfiddle.net/KUtY5/483/.
You bold like this
$("#nav a").css("font-weight", 400); // First you make them thin
$(this).css("font-weight", 800); // Than you make them bold
You put placeholder like this
<div id="placeholder">
Placeholder
</div>
$("#placeholder").hide();
On the other hand I recommend you not to hide menu container. Rather hide the elements inside the menu_container. So you can put a plcaeholder in menu container and you can hide it.
To figure this out 2 questions must be asked / solved
how do you normally make text bold on a page... css right?
where do you want those styles to be defined? There are 2 places:
a. You can define it inside the javascript.
b. You can define it inside the projects css through normal methods (inline, external, embedded).
What's the difference? If you define it inside the javascript the code is self-contained. What i mean by that is you can copy/paste the JS code from one project to the next and you don't need to worry about copying related styles from the stylesheets or other sources because it's all in the JQuery that you've written.
In contrast if you define it outside the javascript in the regular places the code may not be self-contained however some find it easier to manage in the scope of that particular project because all your css is contained in one place (external stylesheet typically).
If you want to take option a, see the .css() method
If you want to take option b, see the style manipulation (toggle class in particular)
Note the examples in the above references should get you 90% of the way to understanding it.
Some final words. Learn Jquery, but i advise you to stay away from it as much as possible because it implements DOM thrashing instead of DOM caching (sizzle engine).
This video series will briefly go into why Jquery sucks for performance in the first video and the rest of the series is about how to create modular vanilla JS.
JQuery goes back and searches the DOM every time you need to make a change that is what
$.(*element*) is doing instead of just caching it.
The more nodes you have in the DOM the more processing power is used searching (because it has to go through the entire tree).
Then on top of that the more elements you have to make changes to (say if you use a css class selector) you have to add even more processing on top of that.
All this is fine if you're on a desktop, but what about a mobile platform? Where would you get all this processing power from?... It doesn't exist.

How to apply different stylesheets based on user on wordpress

I have a Wordpress self-hosted website for extra content about a series of books. On the near future, I intend to let the user declare which book(s) from the series he/she has read - and whilst it would be just for fun, I thought I could take this idea further.
The thing is, since some content on generic pages might be considered spoilers if they haven't read some of the books, I would like to apply a different stylesheet based on logged in user - so if the user has read up until book 2, a stylesheet would be loaded in such a way that any paragraph with the class "book3" or higher would be automatically converted into a hidden paragraph with a spoiler alert. But if the user has already read book 3, a different stylesheet would be loaded; one that wouldn't do that, and would let the user see the content normally.
I reckon this could also be done with javascript, but I suppose CSS would be easier? I'd be up for answers based on both methods, of course (including jQuery regarding javascript).
You can accomplish this without javascript.
One way to do this would be to add an additional class to the body if a user has read a particular book. For example, if a user has read book 1 you would want to add the class 'read-book-1' to the body. You can accomplish this in WordPress using the body_class filter.
https://codex.wordpress.org/Plugin_API/Filter_Reference/body_class
Here is a stripped down example:
add_filter('body_class', function($classes) {
if(/* if user has read book 1 */) {
$classes[] = 'read-book-1';
}
return $classes;
}
Then, assuming your markup looks like this:
<p class="spoiler book-1">...</p>
You could override your spoiler style if the user has read book 1:
p.spoiler {
visibility:hidden;
}
.read-book-1 p.spoiler.book-1 {
visibility:visible;
}
Note that I'm just assuming you're using visibility: hidden; to hide spoiler text. There is more than one way to do this.
If you wanted to incorporate additional features, such as a link to reveal spoiler text, then you would need javascript. If you just wanted to reveal spoiler text on hover you can do it with just css:
p.spoiler:hover {
visibility:visible;
}
To load a different CSS file according to a Javascript variable you can use a switch statement. Each case would use document.write() to write in the . This needs to be done before the body of the HTML loads so you don't overwrite anything. The MDN page which is linked to explains the details for this.
switch (variable) {
case possibility1:
document.write("<link rel='stylesheet' href='___.css' type='text/css'>");
break;
case possibility2: ...
default: ...
}
Another alternative could be applying a class to spoilers. If content would be spoiled for book 2 and you know book2.hasRead === false, the hidden class would be applied to that content. Using element.classlist and getElementById is the easiest way that comes to mind.
Using a similar method to how responsive design uses different CSS for different situations could work as well but I don't know enough about that to advise how to do so.

jQuery Conflict with Collapsible List

I'm currently using a small jQuery script, included infra, to make lists with the class collapsible-list collapsible/expandable. By default, the script collapses the list (and a bit of CSS, also included infra, makes this easily noticeable for visitors) and then expands it when a visitor clicks on expandable elements.
Here are the scripts:
The jQuery
jQuery(function($) {
function prepareList() {
$('.collapsible-list').find('li:has(ul)')
.click( function(event) {
if (this == event.target) {
$(this).toggleClass('collapsible-list-expanded');
$(this).children('ul').toggle('medium');
}
return false;
})
.addClass('collapsible-list-collapsed')
.children('ul').hide();
};
$(document).ready( function() {
prepareList()
});
});
The CSS
/* Collapsible Lists */
.collapsible-list, .collapsible-list ul, .collapsible-list li {
list-style: none;
}
.collapsible-list .collapsible-list-collapsed:before {
content: "+ ";
font-weight: bold;
color: #00AA00;
}
.collapsible-list .collapsible-list-expanded:before {
content: "- ";
font-weight: bold;
color: #AA0000;
}
The CSS is included for the sake of completeness, it functions exactly as expected. The issue lies with the jQuery (and likely some other script on the page, which I cannot seem to isolate).
The script does collapse all but the top-level sections of any list given the collapsible-list class, but that's where the proper functionality ends. Now, the script behaves as expected in my test environment (and also in JSFiddle); however, once the script is implemented in my site, the lists collapse, expand one click, but, instead of staying expanded after being clicked (as they should), they immediately collapse again. This, quite obviously, renders the script useless once implemented, as any list on which it is invoked by the collapsible-list class is made unusable for visitors.
Here is a link to a page where it is currently implemented: http://wpmudev.docs.omnifora.com/docs/plugins/wpmu-dev-dashboard/. Now, I'm sure there is a script conflict somewhere, but I cannot seem to figure out what script is conflicting.
Additional Information
This site uses Bootstrap, which, I suspect, might be the source of the conflict.
Updates
I've narrowed down the potential conflicts, and it seems that Bootstrap may not be the culprit. Here is a JSFiddle with the list behaving as expected within two different Bootstrap panel setups (nested within panel-body and nested within panel): enter link description here.
I've now managed to get the list working except as to formatting. The jQuery conflict seems to have been caused by a slight error in the way one script was calling another, which leads to the collapsible-list.js file being loaded twice. Here's a working JSFiddle: enter link description here
It might work to add a .toggleClass('collapsible-list-collapsed'); in your click function.
After fiddling with things for a few hours, I was able to resolve the conflicts:
One of the script references was improperly formatted, and that was resulting in two calls to collapsible-list.js, which was causing the unexpected immediately-contract-upon-expansion behaviour.
The original script used just expanded and collapsed as classes, which was interacting with another set of rules and causing unexpected behaviour; the modified script uses more specific classes, which avoid such conflicts.
and then amend the CSS:
Someone had thought it wise to put padding: 0; onto ul and li elements in the theme's primary CSS file; this was, obviously, easily overridden with a more specific rule for .collapsible-list.
The result is that the page is now functioning as expected: http://wpmudev.docs.omnifora.com/docs/plugins/wpmu-dev-dashboard/.

Expand/Collapse Text

The code below works fine with ONE Reveal/Hide Text process
<div class="reveal">Click Here to READ MORE...</div>
<div style="display:none;">
<div class="collapse" style="display:none;">Collapse Text</div>
However if this code is duplicated multiple times, the Collapse Text shows up and doesn't disappear and in fact conflicts with the Expand to reveal even more text instead of collapsing as it should.
In this http://jsfiddle.net/syEM3/4/ click on any of the Click Here to READ MORE...
Notice how the Collapse Text shows up at the bottom of the paragraphs and doesn't disappear. Click on the Collapse and it reveal more text.
How do I prevent this and getting to work as it should?
The two slideDown function calls are not specific to the .reveal and/or .collapse that you are currently doing. i.e.
$(".collapse").slideDown(100);
will find all the elements with the class .collapse on the page, and slide them down. irrespective of what element you just clicked.
I would change the slideDown call to be relavant to the element you just clicked i.e. something like this
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(this).next().next(".collapse").slideToggle(100);
});
in your code
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(".collapse").slideDown(100);
});
$('.collapse').click(function() {
$(this).slideUp(100);
$(this).prev().slideToggle();
$(".reveal").slideDown(100);
});
this two rows doesn’t do what you want as they act on all elements of the specified class
$(".reveal").slideDown(100);
$(".collapse").slideDown(100);
When you do $(".collapse").slideDown(100);, jQuery runs slideDown on everything with the .collapse class, not just the one that's related to your current this. To fix this, refer to the collapse based on its location to $(this).
Do do this, use something like $(this).siblings(".collapse").slideDown(100);
Note that this particular selector will only work if you enclose each text block in its own div. With each text element in its own div, like you have it now, .siblings(".collapse"), which selects all the siblings of $(this) with the collapse class, will still select both of the collapse elements.
Okay, I think you should take a different approach to your problem.
See, jQuery basically has two purposes:
Selecting one or more DOM elements from your HTML page
manipulate the selected elements in some way
This can be repeated multiple times, since jQuery functions are chainable (this means you can call function after function after function...).
If I understood your problem correctly, you are trying to build a list of blog posts and only display teasers of them.
After the user clicks the "read more" button, the complete article gets expanded.
Keep in mind: jQuery selects your elements very much like CSS would do. This makes it extremely easy to
come up with a query for certain elements, but you need to structure your HTML in a good way, like
you would do for formatting reasons.
So I suggest you should use this basic markup for each of your articles (heads up, HTML5 at work!):
<article class="article">
<section class="teaser">
Hey, I am a incredible teaser text! I just introduce you to the article.
</section>
<section class="full">
I am the articles body text. You should not see me initially.
</section>
</article>
You can replace the article and section elements with div elements if you like to.
And here is the CSS for this markup:
/* In case you want to display multiple articles underneath, separate them a bit */
.article{
margin-bottom: 50px;
}
/* we want the teaser to stand out a bit, so we format it bold */
.teaser{
font-weight: bold;
}
/* The article body should be a bit separated from the teaser */
.full{
padding-top: 10px;
}
/* This class is used to hide elements */
.hidden{
display: none;
}
The way we created the markup and CSS allows us to put multiple articles underneath.
Okay, you may have noticed: I completely omitted any "read more" or "collapse" buttons. This is done by intention.
If somebody visits the blog site with javascript disabled (maybe a search engine, or a old mobile which doesn't support JS or whatever),
the logic would be broken. Also, many text-snippets like "read more" and "collapse" are not relevant if they don't actually do anything and are not part of the article.
Initially, no article body is hidden, since we didn't apply the hidden css class anywhere. If we would
have embedded it in the HTML and someone really has no JavaScript, he would be unable to read anything.
Adding some jQuery magic
At the bottom of the page, we are embedding the jQuery library from the google CDN.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
This is a best practice and will normally speed up your page loading time. Since MANY websites are embedding
jQuery through this URL, chances are high that its already in the visitors browser cache and doesn't have
to be downloaded another time.
Notice that the http: at the beginning of the URL is omitted. This causes browsers to use the pages current protocol,
may it be http or https. If you would try and embed the jQuery lib via http protocol on a https website, some browsers will refuse to download the file from a unsecure connection.
After you included jQuery into the page, we are going to add our logic into a script tag. Normally we would
save the logic into a separate file (again caching and what not all), but this time a script block will do fine.
Finally some JavaScript
At first, we want to hide all elements with the css-class full, since only teasers should remain displayed. This is very easy with jQuery:
$('.full').hide();
The beginning of the script $('.full') tells jQuery: I need all elements with the CSS-class full. Then we call a function on that result, namingly hide() which purpose should be clear.
Okay, in the next step, we want to add some "read more" buttons, next to every teaser. Thats an easy task, too:
$('.teaser').after('<button class="more">Read more</button>');
We now select every element with the css-class teaser and append some HTML code after() each element - a button with the css-class more.
In the next step, we tell jQuery to observe clicks on every one of this freshly created buttons. When a user has clicked, we want to expand the next element with the css-class full after the clicked button.
$('.more').on('click', function(){
//"this" is a reference to the button element!
$(this).slideUp().next('.full').slideDown();
});
Phew, what did we do here?
First, we told jQuery that we wanted to manipulate this, which is a reference to the clicked button. Then we told
jQuery to hide that button (since its not needed anymore) slowly with slideUp().
We immediately continued telling jQuery what to do: Now take the next() element (with the css-class full) and make it visible by sliding it down with slideDown().
Thats the power of jQuerys chaining!
Hiding again
But wait, you wanted to be able to collapse the articles again! So we need a "collapse" button, too and
some more JavaScript:
$('.full').append('<button class="collapse">Collapse text</button>');
Note: we didn't use the after() function to add this button, but the append() function to place the button
INSIDE every element with the css-class full, rather than next to it. This is because we want the
collapse buttons to be hidden with the full texts, too.
Now we need to have some action when the user clicks one of those buttons, too:
$('.collapse').on('click', function(){
$(this).parent().slideUp().prev('.more').slideDown();
});
Now, this was easy: We start with the button element, move the focus to its parent() (which is the element that contains the full text) and tell jQuery to hide that element by sliding it up with slideUp().
Then we move the focus from the full-text container to its previous element with the css-class more, which is its expanding button that has been hidden when expanding the text. We slowly show that button again by calling slideDown().
Thats it :)
I've uploaded my example on jsBin.

Applying layer of JS before the HTML loads

I have a .html loaded to clients. On it, jQuery does some modifications.
The problem is that the page loads in two steps: first the original .html, then, a fraction of a second later, the modified .html.
This approach causes jerkyness. Is there a way to show the .html only once JavaScript has acted upon it?
If you MUST do this, then something like this:
Javascript:
$(document).ready(function(){
myfunction();
$("#wrapper").show();
}
CSS:
div#wrapper{ display: none; }
HTML:
<div id="wrapper">
<!-- my page stuff that i dont want to be jerky -->
</div>
However, I would raelly advise that you find a way to apply the styles/data to the page before you generate it (e.g. with PHP, ASP etc.),
You can use CSS to set default properties of the parts you are changing, if these are stylistic changes and not HTML changes.
You can also use jQuery's .load() to reload page fragments instead of the whole page.
Or, use css to set body { display: none; } and using (document).ready() to $('body').show().

Categories