jQuery CSS-Plugin, das den berechneten Elementstil zurückgibt, um dieses Element zu pseudoklonen?

68

Ich suche nach einer Möglichkeit, mit jQuery ein Objekt mit berechneten Stilen für das erste übereinstimmende Element zurückzugeben. Ich könnte dieses Objekt dann an einen anderen Aufruf der CSS-Methode von jQuery übergeben.

Mit width kann ich beispielsweise Folgendes tun, damit die beiden Divs dieselbe Breite haben:

$('#div2').width($('#div1').width());

Es wäre schön, wenn ich eine Texteingabe wie eine vorhandene Spanne aussehen lassen könnte :

$('#input1').css($('#span1').css());

Dabei gibt .css () ohne Argument ein Objekt zurück, das an .css (obj) übergeben werden kann .

(Ich kann kein jQuery-Plugin dafür finden, aber es scheint zu existieren. Wenn es nicht existiert, werde ich mein Plugin unten in ein Plugin verwandeln und es mit allen von mir verwendeten Eigenschaften veröffentlichen.)

Grundsätzlich möchte ich bestimmte Elemente pseudoklonen, aber ein anderes Tag verwenden . Zum Beispiel habe ich ein li-Element, das ich ausblenden möchte, und ein Eingabeelement darüber, das gleich aussieht. Wenn der Benutzer tippt, sieht es so aus, als würde er das Element inline bearbeiten .

Ich bin auch offen für andere Ansätze für dieses Pseudo-Klonproblem zur Bearbeitung. Irgendwelche Vorschläge?

Folgendes habe ich derzeit. Das einzige Problem ist nur, alle möglichen Stile zu bekommen. Dies könnte eine lächerlich lange Liste sein.


jQuery.fn.css2 = jQuery.fn.css;
jQuery.fn.css = function() {
    if (arguments.length) return jQuery.fn.css2.apply(this, arguments);
    var attr = ['font-family','font-size','font-weight','font-style','color',
    'text-transform','text-decoration','letter-spacing','word-spacing',
    'line-height','text-align','vertical-align','direction','background-color',
    'background-image','background-repeat','background-position',
    'background-attachment','opacity','width','height','top','right','bottom',
    'left','margin-top','margin-right','margin-bottom','margin-left',
    'padding-top','padding-right','padding-bottom','padding-left',
    'border-top-width','border-right-width','border-bottom-width',
    'border-left-width','border-top-color','border-right-color',
    'border-bottom-color','border-left-color','border-top-style',
    'border-right-style','border-bottom-style','border-left-style','position',
    'display','visibility','z-index','overflow-x','overflow-y','white-space',
    'clip','float','clear','cursor','list-style-image','list-style-position',
    'list-style-type','marker-offset'];
    var len = attr.length, obj = {};
    for (var i = 0; i < len; i++) 
        obj[attr[i]] = jQuery.fn.css2.call(this, attr[i]);
    return obj;
}

Bearbeiten: Ich benutze jetzt seit einer Weile den obigen Code. Es funktioniert gut und verhält sich mit einer Ausnahme genau wie die ursprüngliche CSS-Methode: Wenn 0 Argumente übergeben werden, wird das berechnete Stilobjekt zurückgegeben.

Wie Sie sehen, wird sofort die ursprüngliche CSS-Methode aufgerufen, wenn dies der Fall ist. Andernfalls werden die berechneten Stile aller aufgelisteten Eigenschaften abgerufen (aus der Liste der berechneten Stile von Firebug). Obwohl es eine lange Liste von Werten gibt, ist es ziemlich schnell. Hoffe, es ist nützlich für andere.

Keith Bentrup
quelle
4
Ich frage mich, ob Ihre Frage mit CSS-Klassen besser gelöst werden kann.
Spender
Ich würde auch gerne eine Lösung dafür sehen, aber ich schlage vor, nicht jeden berechneten Stil durchzugehen. Wenn ich eine nicht-jquery-Methode verwende, um berechnete Stile abzurufen, dauert es ungefähr 1-1,5 ms, um nur eine Eigenschaft zu erhalten. Das Durchlaufen eines Arrays, das jede Eigenschaft abruft, kann zu einer beträchtlichen Verzögerungszeit führen.
Ian Elliott
@ Ian, der das Obige auf meinem über 2 Jahre alten Laptop profiliert hat, hat ungefähr 50 Eigenschaften in 7 ms geklont.
Keith Bentrup
1
Sie müssen den jQuery.fnNamespace nicht mit verschmutzen css2. Sie können die ursprüngliche Funktion einfach umwandeln, wenn Sie einen Verschluss verwenden. Siehe meine Bearbeitung hier: stackoverflow.com/a/1471256/399649
Justin Morgan

Antworten:

61

Zwei Jahre zu spät, aber ich habe die Lösung, die Sie suchen. Hier ist ein Plugin, das ich geschrieben habe (indem ich die Funktion eines anderen Mannes in das Plugin-Format verpackt habe), das genau das tut, was Sie wollen, aber alle möglichen Stile in allen Browsern erhält , sogar im IE.

jquery.getStyleObject.js:

/*
 * getStyleObject Plugin for jQuery JavaScript Library
 * From: http://upshots.org/?p=112
 *
 * Copyright: Unknown, see source link
 * Plugin version by Dakota Schneider (http://hackthetruth.org)
 */

(function($){
    $.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            }
            style = window.getComputedStyle(dom, null);
            for(var i=0;i<style.length;i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            }
            return returns;
        }
        if(dom.currentStyle){
            style = dom.currentStyle;
            for(var prop in style){
                returns[prop] = style[prop];
            }
            return returns;
        }
        return this.css();
    }
})(jQuery);

Die grundlegende Verwendung ist ziemlich einfach:

var style = $("#original").getStyleObject(); // copy all computed CSS properties
$("#original").clone() // clone the object
    .parent() // select it's parent
    .appendTo() // append the cloned object to the parent, after the original
                // (though this could really be anywhere and ought to be somewhere
                // else to show that the styles aren't just inherited again
    .css(style); // apply cloned styles

Hoffentlich hilft das.

Dakota
quelle
2
Nur müssen Änderungen var camel = prop.replace(/\-([a-z])/, camelize);an var camel = prop.replace(/\-([a-z])/g, camelize);und es funktioniert wunderbar. Vielen Dank!
Ivan
1
@ Dakota: +1. aber ich habe am Ende Klammern hinzugefügt - getStyleObject();; weil Stil sonst eine Funktion und nicht ein Objekt mit allen Stilen enthalten würde;)
Mottie
1
Gibt es eine Chance / Möglichkeit, dies mit neueren CSS3-Eigenschaften wie Transformieren, Übersetzen usw. zum Laufen zu bringen? Das geht nicht
TrySpace
Gibt es eine Möglichkeit, auch alle inneren Elemente mit den entsprechenden Stilen abzurufen? Die inneren Elemente bekommen nicht ihren Stil.
Halbblutprinz
@HalfBloodPrince suchte nach dem gleichen Thema, siehe die Antwort des Nonkonformisten auf dieser Seite.
Majick
23

Es ist nicht jQuery, aber in Firefox, Opera und Safari können Sie window.getComputedStyle(element)die berechneten Stile für ein Element abrufen und in IE <= 8 verwenden element.currentStyle. Die zurückgegebenen Objekte sind in jedem Fall unterschiedlich, und ich bin mir nicht sicher, wie gut sie mit Elementen und Stilen funktionieren, die mit Javascript erstellt wurden, aber vielleicht sind sie nützlich.

In Safari können Sie Folgendes tun, was ziemlich ordentlich ist:

document.getElementById('b').style.cssText = window.getComputedStyle(document.getElementById('a')).cssText;
Richard M.
quelle
Vielen Dank. Nachdem Sie sich eingehender mit diesem Problem befasst haben, werden diese Methoden von jquery in der CSS-Methode verwendet, sodass neu geschrieben wird, was bereits vorhanden ist.
Keith Bentrup
Erwähnenswert window.getComputedStyleist jetzt ziemlich gut unterstützt: caniuse.com/getcomputedstyle
abernier
5

Ich weiß nicht, ob Sie mit den Antworten, die Sie bisher erhalten haben, zufrieden sind, aber ich war es nicht und meine mögen Ihnen auch nicht gefallen, aber es kann jemand anderem helfen.

Nachdem ich darüber nachgedacht habe, wie man die Stile von Elementen von einem zum anderen "klont" oder "kopiert", habe ich festgestellt, dass es nicht sehr optimal war, n zu durchlaufen und auf n2 anzuwenden, aber wir bleiben irgendwie dabei .

Wenn Sie mit diesen Problemen konfrontiert sind, müssen Sie selten ALLE Stile von einem Element in ein anderes kopieren. In der Regel haben Sie einen bestimmten Grund, "einige" Stile anzuwenden.

Hier ist, worauf ich zurückgegriffen habe:

$.fn.copyCSS = function( style, toNode ){
  var self = $(this);
  if( !$.isArray( style ) ) style=style.split(' ');
  $.each( style, function( i, name ){ toNode.css( name, self.css(name) ) } );
  return self;
}

Sie können eine durch Leerzeichen getrennte Liste von CSS-Attributen als erstes Argument und den Knoten, auf den Sie sie klonen möchten, als zweites Argument übergeben:

$('div#copyFrom').copyCSS('width height color',$('div#copyTo'));

Was auch immer danach "falsch ausgerichtet" zu sein scheint, ich werde versuchen, es mit Stylesheets zu korrigieren, um meine Js nicht mit zu vielen fehlgeschlagenen Ideen zu überladen.

Quickredfox
quelle
3

Nachdem ich nun einige Zeit hatte, mich mit dem Problem zu befassen und besser zu verstehen, wie die interne CSS-Methode von jQuery funktioniert, scheint das, was ich gepostet habe, für den von mir erwähnten Anwendungsfall gut genug zu funktionieren.

Es wurde vorgeschlagen, dass Sie dieses Problem mit CSS lösen können, aber ich denke, dies ist eine allgemeinere Lösung, die auf jeden Fall funktioniert, ohne dass Sie Klassen zum Entfernen entfernen oder Ihr CSS aktualisieren müssen.

Ich hoffe, andere finden es nützlich. Wenn Sie einen Fehler finden, lassen Sie es mich bitte wissen.

Keith Bentrup
quelle
3

Ich mag deine Antwort Quickredfox. Ich musste etwas CSS kopieren, aber nicht sofort, also habe ich es geändert, um den "toNode" optional zu machen.

$.fn.copyCSS = function( style, toNode ){
  var self = $(this),
   styleObj = {},
   has_toNode = typeof toNode != 'undefined' ? true: false;
 if( !$.isArray( style ) ) {
  style=style.split(' ');
 }
  $.each( style, function( i, name ){ 
  if(has_toNode) {
   toNode.css( name, self.css(name) );
  } else {
   styleObj[name] = self.css(name);
  }  
 });
  return ( has_toNode ? self : styleObj );
}

Wenn Sie es so nennen:

$('div#copyFrom').copyCSS('width height color');

Anschließend wird ein Objekt mit Ihren CSS-Deklarationen zurückgegeben, das Sie später verwenden können:

{
 'width': '140px',
 'height': '860px',
 'color': 'rgb(238, 238, 238)'
}

Danke für den Ausgangspunkt.

HexInteractive
quelle
3

Mehrzweck .css()

Verwendung

$('body').css();        // -> { ... } - returns all styles
$('body').css('*');     // -> { ... } - the same (more verbose)
$('body').css('color width height')  // -> { color: .., width: .., height: .. } - returns requested styles
$('div').css('width height', '100%')  // set width and color to 100%, returns self
$('body').css('color')  // -> '#000' - native behaviour

Code

(function($) {

    // Monkey-patching original .css() method
    var nativeCss = $.fn.css;

    var camelCase = $.camelCase || function(str) {
        return str.replace(/\-([a-z])/g, function($0, $1) { return $1.toUpperCase(); });
    };

    $.fn.css = function(name, value) {
        if (name == null || name === '*') {
            var elem = this.get(0), css, returns = {};
            if (window.getComputedStyle) {
                css = window.getComputedStyle(elem, null);
                for (var i = 0, l = css.length; i < l; i++) {
                    returns[camelCase(css[i])] = css.getPropertyValue(css[i]);
                }
                return returns;
            } else if (elem.currentStyle) {
                css = elem.currentStyle;
                for (var prop in css) {
                    returns[prop] = css[prop];
                }
            }
            return returns;
        } else if (~name.indexOf(' ')) {
            var names = name.split(/ +/);
            var css = {};
            for (var i = 0, l = names.length; i < l; i++) {
                css[names[i]] = nativeCss.call(this, names[i], value);
            }
            return arguments.length > 1 ? this : css;
        } else {
            return nativeCss.apply(this, arguments);
        }
    }

})(jQuery);

Die Hauptidee stammt aus den Antworten von Dakota & HexInteractive .

enttäuscht
quelle
1
Das ist großartig! Ich könnte eine Verbesserung der Antwort von HexInteractive vorschlagen : Fügen Sie ein hinzu else if(name.split(' ').length > 1)und erstellen Sie ein Objekt mit durch Leerzeichen getrennten CSS-Werten für$('body').css('color padding width') // -> { ... }
Michael
Vielen Dank. Leerzeichengetrennte Syntax hinzugefügt. Bitte testen.
disfated
Tolle Funktion! Aber wahrscheinlich müssen Sie am Anfang auch eine Bedingung für undefined hinzufügen: if (name == undefined || name == null || name === '*'), um richtig zu funktionieren.
GigaDIE
"name == null" behandelt auch undefiniert. Сheck: undefined == null => gibt true zurück.
am
Vielen Dank, das Hinzufügen von $ ('div'). CSS ('*: not (display)') wäre sehr praktisch
Mike
3

Ich wollte nur eine Erweiterung des von Dakota eingereichten Codes hinzufügen.

Wenn Sie ein Element mit allen darauf angewendeten Stilen und allen untergeordneten Elementen klonen möchten, können Sie den folgenden Code verwenden:

/*
 * getStyleObject Plugin for jQuery JavaScript Library
 * From: http://upshots.org/?p=112
 *
 * Copyright: Unknown, see source link
 * Plugin version by Dakota Schneider (http://hackthetruth.org)
 */

(function($){
    $.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            }
            style = window.getComputedStyle(dom, null);
            for(var i=0;i<style.length;i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            }
            return returns;
        }
        if(dom.currentStyle){
            style = dom.currentStyle;
            for(var prop in style){
                returns[prop] = style[prop];
            }
            return returns;
        }
        return this.css();
    }


    $.fn.cloneWithCSS = function() {
        var styles = {};

        var $this = $(this);
        var $clone = $this.clone();

        $clone.css( $this.getStyleObject() );

        var children = $this.children().toArray();
        var i = 0;
        while( children.length ) {
            var $child = $( children.pop() );
            styles[i++] = $child.getStyleObject();
            $child.children().each(function(i, el) {
                children.push(el);
            })
        }

        var cloneChildren = $clone.children().toArray()
        var i = 0;
        while( cloneChildren.length ) {
            var $child = $( cloneChildren.pop() );
            $child.css( styles[i++] );
            $child.children().each(function(i, el) {
                cloneChildren.push(el);
            })
        }

        return $clone
    }

})(jQuery);

Dann können Sie einfach tun: $clone = $("#target").cloneWithCSS()

Nonkonformist
quelle
2

Großartige Funktion des OP. Ich habe es leicht modifiziert, damit Sie auswählen können, welche Werte zurückgegeben werden sollen.

(function ($) {
    var jQuery_css = $.fn.css,
        gAttr = ['font-family','font-size','font-weight','font-style','color','text-transform','text-decoration','letter-spacing','word-spacing','line-height','text-align','vertical-align','direction','background-color','background-image','background-repeat','background-position','background-attachment','opacity','width','height','top','right','bottom','left','margin-top','margin-right','margin-bottom','margin-left','padding-top','padding-right','padding-bottom','padding-left','border-top-width','border-right-width','border-bottom-width','border-left-width','border-top-color','border-right-color','border-bottom-color','border-left-color','border-top-style','border-right-style','border-bottom-style','border-left-style','position','display','visibility','z-index','overflow-x','overflow-y','white-space','clip','float','clear','cursor','list-style-image','list-style-position','list-style-type','marker-offset'];
    $.fn.css = function() {
        if (arguments.length && !$.isArray(arguments[0])) return jQuery_css.apply(this, arguments);
        var attr = arguments[0] || gAttr,
            len = attr.length,
            obj = {};
        for (var i = 0; i < len; i++) obj[attr[i]] = jQuery_css.call(this, attr[i]);
        return obj;
    }
})(jQuery);

Wählen Sie die gewünschten Werte aus, indem Sie Ihr eigenes Array angeben: $().css(['width','height']);

Shea
quelle
0
$.fn.cssCopy=function(element,styles){
var self=$(this);
if(element instanceof $){
    if(styles instanceof Array){
        $.each(styles,function(val){
            self.css(val,element.css(val));
        });
    }else if(typeof styles===”string”){
        self.css(styles,element.css(styles));
    }
}
return this;
};

Verwenden Sie ein Beispiel

$("#element").cssCopy($("#element2"),['width','height','border'])
rterrani
quelle