Newsgroup: it.comp.lang.javascript
Titolo: simulare l'hover di un tag a

Sto lavorando su un oggetto svg prodotto da una liberia grafica 
(
http://www.highcharts.com/
, ma la cosa varrebbe per qualunque altra 
libreria che gestisce oggetti svg).
Quando attivo un elemento di questo oggetto svg vorrei che un certo link 
nella pagina web venisse evidenziato come se il cursore ci fosse sopra.
 
Ammettendo di avere pieno controllo del file css, potrei fare cosï¿œ:
 
a:hover, a.speciale {
... // link evidenziato
}
 
quindi quando attivo l'elemento aggiungo la classe /speciale/ al link e 
quando lo disattivo la elimino.
 
Perï¿œ potrei trovarmi nella situazione di non conoscere a priori dove e 
come sono impostati gli attributi di a:hover.
 
Vorrei allora sapere se esiste (va bene anche jQuery) la possibilitï¿œ di 
"clonare" tutta la definizione di a:hover, applicarla a un link 
specifico, e quindi toglierla.
 
Qualcosa del tipo:
 
var attrA=$("a:hover").css.copy();
$("#mio_tag_a").css.apply(attrA);
...
$("#mio_tag_a").css.apply(attrA).remove();
 
Spero di essermi spiegato in maniera comprensibile.
 
Grazie.
 

 specifico, e quindi toglierla.
 
No, JS non ha accesso ai css, a meno che tu non voglia metterti a parsarli 
tutti.
 
Ma non ho capito, se hai il controllo totale del css, perché non puoi/
vuoi usare o creare una classe apposita.
 
Bye.

Alessandro Pellizzari ha scritto:

No, posso non avere il controllo totale del css quando il mio codice è 
"ospite" della pagina di un altro sito.
Nel frattempo avevo fatto qualche ricerca, e anch'io mi sono scontrato 
col fatto che non è possibile:
http://stackoverflow.com/questions/14590506/clone-css-class
 
 
 

 tutti.
 
In realtà sí, JS ha accesso ai CSS. E a volte fa comodo accedere ai CSS
per poter modificare qualcosa senza dover fare il traversing di tutto il
DOM.
Ovviamente questo è un caso particolare, e quindi deve accedere alle
regole CSS per filtrarle, in modo da ottenere solo quelle interessate.
Ma non è che deve fare il parse dei fogli di stile.

 specifico, e quindi toglierla.
 
Dunque, al momento che io sappia non c'è ancora niente di simile
embedded. Nei browser relativamente moderni, puoi fare qualcosa.
 
Prima ci servono un po' di funzioni generiche:
 
    var generic = Function.call.bind(Function.bind, Function.call);
    var forEach = Array.forEach || generic(Array.prototype.forEach);
    var slice = Array.slice || generic(Array.prototype.slice);
    var matches = generic(
      Element.prototype.matches ||
      Element.prototype.mozMatchesSelector ||
      Element.prototype.webkitMatchesSelector ||
      Element.prototype.msMatchesSelector ||
      Element.prototype.oMatchesSelector
    );
    var partial = function(fn) {
      var args = slice(arguments, 1);
      return function() {
        return fn.apply(this, args.concat(slice(arguments)));
      };
    }
 
Poi dobbiamo recuperare tutte le regole CSS dai fogli di stile:
 
    // Here we get the cssRules across all the stylesheets in one array
    var cssRules = slice(document.styleSheets)
                    .reduce(function(rules, styleSheet) {
                      return rules.concat(slice(styleSheet.cssRules));
                    }, []);
 
A questo punto ci serve una funzione che, dato un elemento DOM, e una
regola CSS, ci dica se tale regola contiene un `:hover` – che sono
quelle che ci interessano – e se si applica all'elemento interessato:
 
    var hoverRulesMatches = function(element, cssRule) {
      var selectorText = cssRule.selectorText;
 
      if (~selectorText.indexOf(':hover'))
        return matches(element, selectorText.replace(/:hover/g, '')) &&
                !matches(element, selectorText);
 
      return false;
    }
 
È una funzione che può essere usata stand alone, ma noi la useremo come
predicate:
 
    // get a reference to an element, then...
    var bar = document.getElementById("bar");
    var barHoverRules = partial(hoverRulesMatches, bar);
 
    // get only the css rules that matches that element
    var elementHoverRules = cssRules.filter(barHoverRules);
 
A questo punto abbiamo una collezione delle regole di `:hover` che si
applicano all'elemento. Dobbiamo però quindi generarci una classe CSS.
Ci serve una funzione che faccia lo "squash" degli stili di tutte le regole:
 
    var squashStyleRules = function (rules) {
      return rules.reduce(function (styles, rule) {
        var style = rule.style;
 
        forEach(style, function(name) {
          var value = style.getPropertyValue(name);
          var priority = style.getPropertyPriority(name);
          styles.push(
            name + ':' + value + (priority ? '!' + priority : '')
          );
        });
 
        return styles;
      }, []).join(';');
    }
 
 
    // need to squash all the styles from the rules obtained
    var barHoverStyle = squashStyleRules(elementHoverRules);
 
Ora possiamo creare una nuova classe. Abbiamo bisogno di un foglio di
stile, probabilmente la cosa migliore è crearsene uno al volo
verificando prima che non sia stato creato, in modo da aggiungere /
cancellare / rimuovere tutti i macelli che uno va a combinarci; qui per
amor di semplicità uso il primo foglio di stile nel documento:
 
    var stylesheet = document.styleSheets[0];
    stylesheet.insertRule('.barHover {' + barHoverStyle + '}', 0);
 
Adesso puoi applicare la classe all'elemento originale ogni volta che ti
serve.
 
Nota che la parte di verifica dell'hover è un po' empirica, e spero che
prima o poi si aggiusti questa mancanza da parte dei browser. C'è un
edge case che non è considerato per semplicità, ma a cui è facile
rimediare, ovvero la funzione che estrapola le regole per l'hover
presume che l'elemento *non* sia già in stato di hover; anche perché
questo codice dovrebbe essere eseguito prima che la pagina visualizzi
alcunché.
 
Ovviamente le performance dipendono anche dai fogli di stile, della
pagina ospite: ma questo vale per tutto, anche quando manipoli il DOM un
conto è scorrersi una pagina con cento elementi, un conto è una pagina
con un godziliardo di elementi.