Wednesday, 23 March 2016

About CSS Selectors

Recently I saw a simple, accepted answer in the forums that tempted me to provide a small extension to the provided answer. This has since spawned two blog post ideas, here is the first (here is the second).

Background

The following question asked how to hide the spinner from a particular page full of small reports refreshed on a timer.
https://community.oracle.com/thread/3908020
The answer was some basic CSS, which could be placed in a variety of locations depending on desired scope.
.u-Processing 
{ 
display:none 
}

The answer is clear, but doesn't show any working. I'm sure many people out there would like to know how to arrive at that answer themselves.

About CSS

If you don't really know what the above code really means, let's start with the basics. CSS allows you to identify an element on a web page, then change one of it's attributes. The selector identifies the component, and the attributes are listed within the brackets. The described example identifies the processing spinner and hides it, but how do we build the relevant syntax ourselves?

I think CSS selectors can be likened to queries against the database. You want to identify a specific component, then change it.

You can invoke the spinner in order to identify it. A function is described in the Oracle APEX API Reference, which can be invoked on demand in your own applications.

You can try this on the login page to apex.oracle.com by opening the browser console window (usually F12) and typing
apex.util.showSpinner()


Invoke the spinner on any APEX page from the browser console
The spinner will display and you will also see output relating to the component itself. If you right click on the spinner and Inspect Element (Chrome et al) you can see more details about the component and what properties it currently has.



You can modify these properties directly and immediately see their impact on the page. You can un-check attributes to disable them, or change their value, often from a discrete list of option. Sizes can be increased/decreased with the arrow keys. I do this all the time to test sizes.

A good reference for these attributes can be found here
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference

Using Selectors

You can identify the spinner using a variety of selector expressions, just like you could find a particular record in the database using a variety of where clauses. Bear in mind some will work faster than others.

Consider the provided solution using .u-Processing. The period prefix means it's looking for a component on the page with the class u-Processing, as per in the definition of the spinner.
<span class="u-Processing" ...

Note I'm referring to the parent span of the .u-Processing-spinner that's highlighted in the image. Setting this to display none will only hide the spinning icon, not the surrounding shaded circle.

If the component had an id, you could use the # prefix to reference the ID, much like using an unique* index.
(*in the web world, the an ID is not guaranteed unique, but should be in best practice)

The element tab can help in determining the required selector. The strip at the bottom shows the path, a form of which can also be retrieved by right-clicking the parent span in the HTML code and selecting 'Copy CSS path' (or Copy Selector, depending on browser version), which may provide a more 'specific' selector, but not necessarily what you need or what.

The spinner may be in different locations on the page depending on context. Performance is another issue, but that's for the next post.
Specificity is important since any given component on the web page could have attributes from a variety of sources, so there is a precedence called CSS Specificity.


Getting what you need

The selector doesn't need to be complicated, just specific. Try it out by either searching for the selector in the code window, or seeing what it returns in a jQuery command entered in the console.
eg:
$('.u-Processing')

Look for a class or ID on the web component you want to manipulate, test out it's uniqueness (so you don't hide anything you shouldn't).

.u-Processing

Then add the attributes you want to set within brackets, multiple attributes are separated by semi-colon.

.u-Processing { display : none; }

This code could then be placed anywhere within APEX that accepts Inline CSS, such as the page attribute. Or it could live in a .css file and associated with your applications.

Want More?

If you want more examples of what you can do with CSS and jQuery in APEX, you may consider my book, jQuery with Oracle APEX. </shameless_plug>

3 comments:

Farhan Siddiqui said...

There is one more way to do that.
When activating a spinner, get DOM object in a javascript variable, as :

var i = apex.util.showSpinner();

Which can later be removed by javascript code :

i.remove();

Value for i can also be saved in a page item, which can later be used in normal javascript code.

Scott Wesley said...

I imagine that will be the more common invocation method, until a deferred action/promise executes.

Can you elaborate on what you mean with your final sentence?

Farhan Siddiqui said...

Yes, using css based selectors along with jquery is great and is very lightweight. But components like spinner or progress bar, which get invoked by calling a function (for e.g. apex.util...) tend to create other components too along with the main component. (for e.g. in older versions of apex, a translucent layer is also created along with progress bar)

Sometimes, these components might get unnoticed or unhandled, by using css and jquery independently on such interdependent components.

So, I prefer using the javascript code style that i mentioned in my previous comment.

Regarding using it in a variable, I could get the value of returned dom element in javascript global variable as :

--- Page Level - Function and Global Variable Declaration --

var g;
function setg(i)
{
g=i;
console.log('g=');
console.log(g);
}
function getg()
{
return g;
}


-- Javascript code on click of button 1 (handled by dynamic action)

var i = apex.util.showSpinner();
console.log(i);
setg(i);


-- Javascript code on click of button 2 (handled by dynamic action)

var i = getg();
console.log(i);
i.remove();

-------

I am a little skeptical about getting value in page item rather than global javascript variable. But I will try it some day.