2011-01-15 17 views
18

Eventuali duplicati:
How to make this JavaScript much faster?jQuery: Evidenzia elemento sotto il cursore del mouse?

che sto cercando di creare un "elemento picker" in jQuery, come Firebug ha. Fondamentalmente, voglio evidenziare l'elemento sotto il mouse dell'utente. Ecco quello che ho finora, ma non sta funzionando molto bene:

$('*').mouseover(function (event) { 
    var $this = $(this); 
    $div.offset($this.offset()).width($this.width()).height($this.height()); 
    return false; 
}); 


var $div = $('<div>') 
    .css({ 'background-color': 'rgba(255,0,0,.5)', 'position': 'absolute', 'z-index': '65535' }) 
    .appendTo('body'); 

Fondamentalmente, io sto iniettando un div nel DOM che ha uno sfondo semi-trasparente. Quindi ascolto l'evento mouseover su ogni elemento, quindi sposta il div in modo che copra quell'elemento.

Al momento, questo rende l'intera pagina diventata rossa non appena si sposta il mouse sulla pagina. Come posso farlo funzionare meglio?

Edit: abbastanza sicuro che il problema è che, non appena il mio mouse tocca la pagina, il corpo viene selezionato, e poi come muovo il mouse intorno, nessuno dei momenti viene passato attraverso il highligher perché la sua overtop di qualunque cosa.


Firebug

Scavando attraverso il codice sorgente di Firebug, ho trovato questo:

drawBoxModel: function(el) 
{ 
    // avoid error when the element is not attached a document 
    if (!el || !el.parentNode) 
     return; 

    var box = Firebug.browser.getElementBox(el); 

    var windowSize = Firebug.browser.getWindowSize(); 
    var scrollPosition = Firebug.browser.getWindowScrollPosition(); 

    // element may be occluded by the chrome, when in frame mode 
    var offsetHeight = Firebug.chrome.type == "frame" ? FirebugChrome.height : 0; 

    // if element box is not inside the viewport, don't draw the box model 
    if (box.top > scrollPosition.top + windowSize.height - offsetHeight || 
     box.left > scrollPosition.left + windowSize.width || 
     scrollPosition.top > box.top + box.height || 
     scrollPosition.left > box.left + box.width) 
     return; 

    var top = box.top; 
    var left = box.left; 
    var height = box.height; 
    var width = box.width; 

    var margin = Firebug.browser.getMeasurementBox(el, "margin"); 
    var padding = Firebug.browser.getMeasurementBox(el, "padding"); 
    var border = Firebug.browser.getMeasurementBox(el, "border"); 

    boxModelStyle.top = top - margin.top + "px"; 
    boxModelStyle.left = left - margin.left + "px"; 
    boxModelStyle.height = height + margin.top + margin.bottom + "px"; 
    boxModelStyle.width = width + margin.left + margin.right + "px"; 

    boxBorderStyle.top = margin.top + "px"; 
    boxBorderStyle.left = margin.left + "px"; 
    boxBorderStyle.height = height + "px"; 
    boxBorderStyle.width = width + "px"; 

    boxPaddingStyle.top = margin.top + border.top + "px"; 
    boxPaddingStyle.left = margin.left + border.left + "px"; 
    boxPaddingStyle.height = height - border.top - border.bottom + "px"; 
    boxPaddingStyle.width = width - border.left - border.right + "px"; 

    boxContentStyle.top = margin.top + border.top + padding.top + "px"; 
    boxContentStyle.left = margin.left + border.left + padding.left + "px"; 
    boxContentStyle.height = height - border.top - padding.top - padding.bottom - border.bottom + "px"; 
    boxContentStyle.width = width - border.left - padding.left - padding.right - border.right + "px"; 

    if (!boxModelVisible) this.showBoxModel(); 
}, 

hideBoxModel: function() 
{ 
    if (!boxModelVisible) return; 

    offlineFragment.appendChild(boxModel); 
    boxModelVisible = false; 
}, 

showBoxModel: function() 
{ 
    if (boxModelVisible) return; 

    if (outlineVisible) this.hideOutline(); 

    Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel); 
    boxModelVisible = true; 
} 

Sembra che si sta utilizzando uno standard div + CSS per disegnare ..... basta per capire come stanno gestendo gli eventi ora ... (questo file è lungo 28K)

C'è anche questo frammento, che presumo recupera l'oggetto appropriato .... anche se non riesco a capire come . Stanno cercando una classe "objectLink-element" ... e non ho idea di cosa sia questo "repObject".

onMouseMove: function(event) 
{ 
    var target = event.srcElement || event.target; 

    var object = getAncestorByClass(target, "objectLink-element"); 
    object = object ? object.repObject : null; 

    if(object && instanceOf(object, "Element") && object.nodeType == 1) 
    { 
     if(object != lastHighlightedObject) 
     { 
      Firebug.Inspector.drawBoxModel(object); 
      object = lastHighlightedObject; 
     } 
    } 
    else 
     Firebug.Inspector.hideBoxModel(); 

}, 

sto pensando che forse quando le MouseMove o mouseover generato l'evento per il nodo evidenziatore posso in qualche modo passarlo insieme, invece? Forse per il nodo sta coprendo ...?


Se posiziono un elemento invisibile sopra di ogni elemento con un più alto z-index del mio evidenziatore, ma do il mio evidenziatore un z-index superiore gli elementi reali ... in teoria, dovrebbe funzionare. Gli elementi invisibili faranno scattare l'evento del mouse, ma l'effetto di evidenziazione sembrerà ancora il suo overtop degli elementi reali.

Ciò significa che ho appena raddoppiato gli elementi DOM, e il posizionamento deve essere corretto. A meno che non sia forse "sollevo" solo gli elementi che l'evidenziatore sta attualmente coprendo ?? Ma questo potrebbe essere ancora ogni elemento>. < Qualcuno mi aiuti!

+3

C'è un brutto catch-22 quando provi a fare cose del genere. Fondamentalmente, non appena si disegna qualcosa sotto il mouse, come risultato del passaggio del mouse su un elemento, il mouse lascia effettivamente l'elemento originale e si trova ora sopra il _new_. –

+0

@ Matt: beh, come facciamo a evitare questo? Firebug non manipola il DOM per il rendering di quelle scatole o ha il proprio motore di rendering? – mpen

+1

Firebug evidenzia elementi in una pagina Web quando si passa il mouse su di essi in Firebug - che è ** separato **. O è così stanco? Quando usi il "click to inspect" delinea solo gli elementi, che possono essere il nocciolo della questione - Firebug potrebbe disegnare 4 elementi separati, uno per ciascun lato della scatola, nel qual caso, il catch-22 che ho citato isn è un problema. –

risposta

1

Il motivo per cui l'intera pagina diventa rossa non appena si passa il mouse è che il codice corrisponde a un evento mouseover per l'elemento body. Puoi evitare che ciò accada selezionando solo i bambini di body.

$('body *').bind(//... 

Ti ha colpito problemi di prestazioni su una pagina con un gran numero di elementi, anche se da quando si legano sarà allegare un listener per ogni singolo elemento abbinato. Prova a dare un'occhiata alla delega degli eventi di jQuery (.delegate(), http://api.jquery.com/delegate/) che ti permette di ascoltare eventi che si propagano fino a un elemento radice e funziona anche per gli eventi del mouse.

+0

Questo non risolve il problema (usando 'body *'), che sposta semplicemente il problema in un elemento leggermente più profondo. Vedi il commento di Matt. Non sono ancora preoccupato per le prestazioni, prendo tutto ciò che funziona. – mpen

+0

Inoltre, la ragione per cui non sto usando live() o delegate() è perché gli elementi futuri dovrebbero essere inclusi ... cioè, non voglio che inizi a selezionare se stesso (motivo per cui l'ho aggiunto dopo che ho legato l'evento). – mpen

+0

Hai provato a controllare l'oggetto evento passato alla richiamata per il target corrente? Se il currentTarget è il tuo evidenziatore div, allora non puoi fare nulla. – andrewle

1

► This è un po 'meno agitato, ma non in grado di rilevare lo spostamento da un elemento genitore di uno dei suoi figli senza prima spostare il mouse completamente off detto genitore.

var $div = $('<div id="highlighter">').css({ 
    'background-color': 'rgba(255,0,0,.5)', 
    'position': 'absolute', 
    'z-index': '65535' 
}).hide().prependTo('body'); 

var $highlit; 

$('*').live('mousemove', function(event) { 
    if (this.nodeName === 'HTML' || this.nodeName === 'BODY') 
    { 
     $div.hide(); 
     return false; 
    } 
    var $this = this.id === 'highligher' ? $highlit : $(this), 

     x = event.pageX, 
     y = event.pageY, 

     width = $this.width(), 
     height = $this.height(), 
     offset = $this.offset(), 

     minX = offset.left, 
     minY = offset.top, 
     maxX = minX + width, 
     maxY = minY + height; 

    if (this.id === 'highlighter') 
    { 
     if (minX <= x && x <= maxX 
      && minY <= y && y <= maxY) 
     { 
      // nada 
     } 
     else 
     { 
      $div.hide(); 
     } 
    } 
    else 
    { 
     $highlit = $this; 
     $div.offset(offset).width($this.width()).height($this.height()).show(); 
    } 
    return false; 
}); 

Speriamo che questo aiuti a far girare la palla. Potresti probabilmente modificare quello che ho scritto per usare document.elementFromPoint(x, y) per verificare se il mouse è stato spostato o meno in un elemento figlio dell'elemento attualmente evidenziato. Non sono abbastanza sveglio per capirlo subito.

In alternativa, se delineare è come mettere in evidenza, è possibile provare l'approccio che ho citato nel mio commento alla domanda originale. Disegna 4 div attorno all'elemento attualmente al passaggio del mouse * - si tratta di un div per ogni lato del riquadro del bordo. Non più elementi di disegno tra te e il tuo contenuto reale!


* scommetterei che document.elementFromPoint(x, y) farà ottenere l'elemento sotto il mouse molto più facile qui.

+0

Arrivarci ... ha ancora bisogno di un po 'di lavoro. Vedrò cosa posso fare. – mpen

2

Posso suggerire un approccio alternativo?

ne dite semplicemente assegnando un background-color a tutti gli elementi figlio della pagina, e poi, su hover(), regolare la background-color di che elemento per aumentare il contrasto?

$('html').children().css('background-color','rgba(0,0,0,0.2)'); 
$('body').children().hover(
    function(){ 
     $(this).css('background-color','#fff'); 
    }, 
    function(){ 
     $(this).css('background-color','rgba(0,0,0,0.2)'); 
    }); 

JS Fiddle demo.

+0

È possibile. Potrei dover seguire questo approccio se non riesco a trovare una soluzione, ma questo è * veramente * mi disturba perché so che ci deve essere un modo per farlo. – mpen

3

di modificare la risposta di David in modo che ripristina correttamente le afterwords colore di sfondo ...

$('body *').live('mouseover mouseout', function(event) { 
    if (event.type == 'mouseover') { 
     $(this).data('bgcolor', $(this).css('background-color')); 
     $(this).css('background-color','rgba(255,0,0,.5)'); 
    } else { 
     $(this).css('background-color', $(this).data('bgcolor')); 
    } 
    return false; 
}); 
25

Tutte queste risposte sono troppo complicate ... Soluzione semplice:

Javascript:

prevElement = null; 
document.addEventListener('mousemove', 
    function(e){ 
     var elem = e.target || e.srcElement; 
     if (prevElement!= null) {prevElement.classList.remove("mouseOn");} 
     elem.classList.add("mouseOn"); 
     prevElement = elem; 
    },true); 

Css:

.mouseOn{ 
    background-color: #bcd5eb !important; 
    outline: 2px solid #5166bb !important; 
} 
Problemi correlati