简体   繁体   中英

DRY - How Can I Combine These jQuery Functions?

ORIGINAL QUESTION:

I'm looking at this ENORMOUS block of code that I KNOW can be slimmed by about 90%. I just can't figure out how to do it.

Here's the block I'm referring to:

$('table.model a').click(function(){
var divForm = [$('div.form')];
$(this).closest('div').not('.close').fadeOut(500).animate({"top": "-414px"}, 100).fadeIn('fast');
    if ($(this).hasClass('tmi48')) {
        $('img.i4').show();
        $('h1.tmi48').show();
    } else if ($(this).hasClass('tmi416')) {
        $('img.i4').show();
        $('h1.tmi416').show();
    } else if ($(this).hasClass('tmi432')) {
        $('img.i4').show();
        $('h1.tmi432').show();
    } else if ($(this).hasClass('tmi464')) {
        $('img.i4').show();
        $('h1.tmi464').show();
    } else if ($(this).hasClass('tmi4s16')) {
        $('img.i4s').show();
        $('h1.tmi4s16').show();
    } else if ($(this).hasClass('tmi4s32')) {
        $('img.i4s').show();
        $('h1.tmi4s32').show();
    } else if ($(this).hasClass('tmi4s64')) {
        $('img.i4s').show();
        $('h1.tmi4s64').show();
    } else if ($(this).hasClass('tmi516')) {
        $('img.i5').show();
        $('h1.tmi516').show();
    } else if ($(this).hasClass('tmi532')) {
        $('img.i5').show();
        $('h1.tmi532').show();
    } else if ($(this).hasClass('tmi564')) {
        $('img.i5').show();
        $('h1.tmi564').show();
    } else if ($(this).hasClass('tmi5c16')) {
        $('img.i5c').show();
        $('h1.tmi5c16').show();
    } else if ($(this).hasClass('tmi5c32')) {
        $('img.i5c').show();
        $('h1.tmi5c32').show();
    } else if ($(this).hasClass('tmi5c64')) {
        $('img.i5c').show();
        $('h1.tmi5c64').show();
    } else if ($(this).hasClass('tmi5s16')) {
        $('img.i5s').show();
        $('h1.tmi5s16').show();
    } else if ($(this).hasClass('tmi5s32')) {
        $('img.i5s').show();
        $('h1.tmi5s32').show();
    } else if ($(this).hasClass('tmi5s64')) {
        $('img.i5s').show();
        $('h1.tmi5s64').show();
    } else if ($(this).hasClass('atti48')) {
        $('img.i4').show();
        $('h1.atti48').show();
    } else if ($(this).hasClass('atti416')) {
        $('img.i4').show();
        $('h1.atti416').show();
    } else if ($(this).hasClass('atti432')) {
        $('img.i4').show();
        $('h1.atti432').show();
    } else if ($(this).hasClass('atti464')) {
        $('img.i4').show();
        $('h1.atti464').show();
    } else if ($(this).hasClass('atti4s16')) {
        $('img.i4s').show();
        $('h1.atti4s16').show();
    } else if ($(this).hasClass('atti4s32')) {
        $('img.i4s').show();
        $('h1.atti4s32').show();
    } else if ($(this).hasClass('atti4s64')) {
        $('img.i4s').show();
        $('h1.atti4s64').show();
    } else if ($(this).hasClass('atti516')) {
        $('img.i5').show();
        $('h1.atti516').show();
    } else if ($(this).hasClass('atti532')) {
        $('img.i5').show();
        $('h1.atti532').show();
    } else if ($(this).hasClass('atti564')) {
        $('img.i5').show();
        $('h1.atti564').show();
    } else if ($(this).hasClass('atti5c16')) {
        $('img.i5c').show();
        $('h1.atti5c16').show();
    } else if ($(this).hasClass('atti5c32')) {
        $('img.i5c').show();
        $('h1.atti5c32').show();
    } else if ($(this).hasClass('atti5c64')) {
        $('img.i5c').show();
        $('h1.atti5c64').show();
    } else if ($(this).hasClass('atti5s16')) {
        $('img.i5s').show();
        $('h1.atti5s16').show();
    } else if ($(this).hasClass('atti5s32')) {
        $('img.i5s').show();
        $('h1.atti5s32').show();
    } else if ($(this).hasClass('atti5s64')) {
        $('img.i5s').show();
        $('h1.atti5s64').show();
    } else if ($(this).hasClass('vzi48')) {
        $('img.i4').show();
        $('h1.vzi48').show();
    } else if ($(this).hasClass('vzi416')) {
        $('img.i4').show();
        $('h1.vzi416').show();
    } else if ($(this).hasClass('vzi432')) {
        $('img.i4').show();
        $('h1.vzi432').show();
    } else if ($(this).hasClass('vzi464')) {
        $('img.i4').show();
        $('h1.vzi464').show();
    } else if ($(this).hasClass('vzi4s16')) {
        $('img.i4s').show();
        $('h1.vzi4s16').show();
    } else if ($(this).hasClass('vzi4s32')) {
        $('img.i4s').show();
        $('h1.vzi4s32').show();
    } else if ($(this).hasClass('vzi4s64')) {
        $('img.i4s').show();
        $('h1.vzi4s64').show();
    } else if ($(this).hasClass('vzi516')) {
        $('img.i5').show();
        $('h1.vzi516').show();
    } else if ($(this).hasClass('vzi532')) {
        $('img.i5').show();
        $('h1.vzi532').show();
    } else if ($(this).hasClass('vzi564')) {
        $('img.i5').show();
        $('h1.vzi564').show();
    } else if ($(this).hasClass('vzi5c16')) {
        $('img.i5c').show();
        $('h1.vzi5c16').show();
    } else if ($(this).hasClass('vzi5c32')) {
        $('img.i5c').show();
        $('h1.vzi5c32').show();
    } else if ($(this).hasClass('vzi5c64')) {
        $('img.i5c').show();
        $('h1.vzi5c64').show();
    } else if ($(this).hasClass('vzi5s16')) {
        $('img.i5s').show();
        $('h1.vzi5s16').show();
    } else if ($(this).hasClass('vzi5s32')) {
        $('img.i5s').show();
        $('h1.vzi5s32').show();
    } else if ($(this).hasClass('vzi5s64')) {
        $('img.i5s').show();
        $('h1.vzi5s64').show();
    } else if ($(this).hasClass('uli48')) {
        $('img.i4').show();
        $('h1.uli48').show();
    } else if ($(this).hasClass('uli416')) {
        $('img.i4').show();
        $('h1.uli416').show();
    } else if ($(this).hasClass('uli432')) {
        $('img.i4').show();
        $('h1.uli432').show();
    } else if ($(this).hasClass('uli464')) {
        $('img.i4').show();
        $('h1.uli464').show();
    } else if ($(this).hasClass('uli4s16')) {
        $('img.i4s').show();
        $('h1.uli4s16').show();
    } else if ($(this).hasClass('uli4s32')) {
        $('img.i4s').show();
        $('h1.uli4s32').show();
    } else if ($(this).hasClass('uli4s64')) {
        $('img.i4s').show();
        $('h1.uli4s64').show();
    } else {
        return;
    }
    divForm[0].animate({"top": "+=557px"}, 500, function(){
        $(this).animate({"top": "-=20px"}, 200);
    }); 
});

Here's the jsFiddle: http://jsfiddle.net/6YUuT/

As you can see, each link is calling the same form, but the header and image change based on which link was clicked.I've tried playing around with shared classes, but I just can't come up with a fix that works.

REVISIONS & NEW QUESTION:

I've ALMOST got all the functions worked out properly, but I ran into my original problem, this time in a different spot. I've got another enormous code block that needs to go away and though it seems almost identical to the last one, it's different enough that I haven't been able to come up with a solution that works.

Here's the jsFiddle: http://jsfiddle.net/9Pr8L/

And the offending/offensive code block:

function linkHide() {
    //var r = /(?:^| )((?:tm|att|vz|sp|ul)(app?|bb?|htc?|lg?|mot?|sam?))(?: |$)/;
    if ($('span.tm').is(':visible') && $('span.app').is(':visible')) {
        $('td.tmapp').show();
    } 
    if ($('span.tm').is(':visible') && $('span.bb').is(':visible')) {
        $('td.tmbb').show();
    }
    if ($('span.tm').is(':visible') && $('span.htc').is(':visible')) {
        $('td.tmhtc').show();
    }
    if ($('span.tm').is(':visible') && $('span.lg').is(':visible')) {
        $('td.tmlg').show();
    }
    if ($('span.tm').is(':visible') && $('span.mot').is(':visible')) {
        $('td.tmmot').show();
    }
    if ($('span.tm').is(':visible') && $('span.sam').is(':visible')) {
        $('td.tmsam').show();
    }
    if ($('span.att').is(':visible') && $('span.app').is(':visible')) {
        $('td.attapp').show();
    }
    if ($('span.att').is(':visible') && $('span.bb').is(':visible')) {
        $('td.attbb').show();
    }
    if ($('span.att').is(':visible') && $('span.htc').is(':visible')) {
        $('td.atthtc').show();
    }
    if ($('span.att').is(':visible') && $('span.lg').is(':visible')) {
        $('td.attlg').show();
    }
    if ($('span.att').is(':visible') && $('span.mot').is(':visible')) {
        $('td.attmot').show();
    }
    if ($('span.att').is(':visible') && $('span.sam').is(':visible')) {
        $('td.attsam').show();
    }
    if ($('span.vz').is(':visible') && $('span.app').is(':visible')) {
        $('td.vzapp').show();
    }
    if ($('span.vz').is(':visible') && $('span.bb').is(':visible')) {
        $('td.vzbb').show();
    }
    if ($('span.vz').is(':visible') && $('span.htc').is(':visible')) {
        $('td.vzhtc').show();
    }
    if ($('span.vz').is(':visible') && $('span.lg').is(':visible')) {
        $('td.vzlg').show();
    }
    if ($('span.vz').is(':visible') && $('span.mot').is(':visible')) {
        $('td.vzmot').show();
    }
    if ($('span.vz').is(':visible') && $('span.sam').is(':visible')) {
        $('td.vzsam').show();
    }
    if ($('span.ul').is(':visible') && $('span.app').is(':visible')) {
        $('td.ulapp').show();
    }
    if ($('span.ul').is(':visible') && $('span.bb').is(':visible')) {
        $('td.ulbb').show();
    }
    if ($('span.ul').is(':visible') && $('span.htc').is(':visible')) {
        $('td.ulhtc').show();
    }
    if ($('span.ul').is(':visible') && $('span.lg').is(':visible')) {
        $('td.ullg').show();
    }
    if ($('span.ul').is(':visible') && $('span.mot').is(':visible')) {
        $('td.ulmot').show();
    }
    if ($('span.ul').is(':visible') && $('span.sam').is(':visible')) {
        $('td.ulsam').show();
    }
};

I've now slimmed down the HTML so that ALL the table data exists in ONE table with variable headers, images and links. I was able to edit wared's regex so that it returns the proper strings, but I could not think of a way to actually USE it.

I assume that there can be only one of these classes per anchor. In this case, a regular expression allows you to extract this class from the class attribute, and extract the image class from it at the same time ( this refers to the anchor element) :

var r = /(?:^| )((?:tm|att|vz|ul)(i\d[a-z]?)\d\d?)(?: |$)/;
var m = $(this).attr('class').match(r);
if (m) {
    $('img.' + m[2]).show();
    $('h1.' + m[1]).show();
} else {
    return;
}

The regular expression :

(?:^| )            the beginning of the string or a whitespace
(...)              capture -> group 1 : the heading class
(?:tm|att|vz|ul)   any of these prefixes, one time
(...)              capture -> group 2 : the image class
i\d[a-z]?          "i" + one digit + any letter, zero or one times
\d\d?              one or two digits
(?: |$)            a whitespace or the end of the string

To summarize :

"tm" or "att" or "vz" or "ul"
+ "i" + one digit + zero or one letter (group 2, example : "i4s")
+ one or two digits

This will match strings like tmi416 , atti4s16 or vzi48 . To make things a bit more clear, you can open your browser console and paste the following code into it :

'foo atti4s16 bar'.match(
    /(?:^| )((?:tm|att|vz|ul)(i\d[a-z]?)\d\d?)(?: |$)/
)

You can see that an array is returned containing both the heading class (index 1 = group 1) and the image class (index 2 = group 2) :

[" atti4s16 ", "atti4s16", "i4s"]

Now that your question is completely different, here's further optimization that again puts most of the data into data structure and then avoids duplicated code by just looping over the data structure:

function linkHide() {

    var atts = ["tm", "att", "vz", "ul"];
    var mfrs = ["app", "bb", "htc", "lg", "mot", "sam"];

    for (var i = 0; i < atts.length; i++) {
        if ($("span." + atts[i]).is(':visible')) {
            for (var j = 0; j < mfrs.length; j++) {
                if ($("span." + mfrs[j]).is(':visible')) {
                    $("td" + "." + atts[i] + mfrs[j]).show();
                }
            }                
        }
    } 
};

This was the answer to an original question which has now been completed changed/edited to something different:

You can put all the classes in an array and walk through the array to condense it down to this:

$('table.model a').click(function(){
    var classList = ['tmi416', 'tmi432', ... put other classes here];
    var divForm = [$('div.form')];
    var self = $(this);
    self.closest('div').not('.close').fadeOut(500).animate({"top": "-414px"}, 100).fadeIn('fast');
    for (var i = 0; i < classList.length; i++) {
        if (self.hasClass(classList[i])) {
            // figure out class name to show
            // algorithm is to remove chars before first "i" and 
                    //    then remove trailing 16,32 or 64
            var name = classList[i].replace(/^.*?i/, "i").replace(/16$|32$|64$/,"");
            $(".img." + name).show();
            $("h1." + classList[i]).show();
            break;
        }
    }
    divForm[0].animate({"top": "+=557px"}, 500, function(){
        $(this).animate({"top": "-=20px"}, 200);
    }); 
});

Or, if you put a common prefix of "xxx-" on the target classnames, you can do it like this without having to build the long list of class names ahead of time:

$('table.model a').click(function(){
    var divForm = [$('div.form')];
    var self = $(this);
    self.closest('div').not('.close').fadeOut(500).animate({"top": "-414px"}, 100).fadeIn('fast');
    var classes = " " + this.className + " ";
    var match = classes.match(/\sxxx-([^ ]+)(i[^ ]+)(\d\d)\s/);
    if (match) {
        $(".img." + match[1] + match[2]).show();
        $("h1." + match[1] + match[2] + match[3]).show();
    }
    divForm[0].animate({"top": "+=557px"}, 500, function(){
        $(this).animate({"top": "-=20px"}, 200);
    }); 
});

If we saw the actual HTML and understood the overall problem, you can probably simplify this much more by changing how you organize your HTML or what classes you use or by putting custom attributes on some elements. But, that requires us seeing much more of what you're doing than just rewriting the one block of code.

Create a map of objects to classes:

IE:

var map = {
  "uli48": {
          img: "i4",
          h1: "uli48"
        }
}

then you can loop along the lines of

var cls, classList = $(this).attr("class").split(" ");

for(var i=0; i<classList.length; i++) {
   cls = classList[i];
   if(map[cls]) {
      $("h1"+cls.h1).show();
      $("img"+cls.img).show();
    }
}

Let's assume the mentioned classes are exclusive. If you depend on the order of the if statements, you'll need some more tweaks.

First, build the dictionary of class of this and the associated classes:

var relatedImgs = { "tmi48" : "i4", ... };

This can be done with sed or an editor macro from your code. Then, you need to find out classes of this :

var thisClasses = $(this).attr('class').split(' ');

Then just try to find the class which is in the relatedImgs dictionary and use the related information:

function findRelated(element) {
    var thisClasses = $(element).attr('class').split(' ');
    for (var i in thisClasses) {
        var imgClass = relatedImgs[thisClasses[i]];
        if (imgClass) {
            return [$("h1." + thisClasses[i]), 
                    $("img." + imgClass)];
        }
    }
    return null;
}

var related = findRelated(this);
if (related !== null) {
    for (var i in related) {
        related.show();
    }
}

This answer is an extension of the following one : https://stackoverflow.com/a/20926579/1636522 .

So, in order to prevent the regular expression from growing more and more as your needs evolve (in other words, to keep things readable and maintainable), you could split it into smaller regular expressions, each one matching a specific device category. This results in something close to the class list solution , but the list is reduced to a few patterns :

var patterns = [
    '(i\\d[a-z]?)\\d\\d?', // iPhone
    '(sgs\\d)',            // Samsung Galaxy S
    '(bbz\\d\\d?)'         // Blackberry Z
];

var i = -1;
while (++i < patterns.length) {
    patterns[i] = new RegExp(
        '(?:^| )((?:tm|att|vz|ul)' + patterns[i] + ')(?: |$)'
    );
}

You could also feed the list manually :

var patterns = [
    /(?:^| )((?:tm|att|vz|ul)(i\d[a-z]?)\d\d?)(?: |$)/,
    /(?:^| )((?:tm|att|vz|ul)(sgs\d))(?: |$)/,
    /(?:^| )((?:tm|att|vz|ul)(bbz\d\d?))(?: |$)/
];

Initialize the click event as follows :

$('a').click(function () {
    var cls = $(this).attr('class'),
        i = -1, 
        m;
    while (++i < patterns.length) {
        if (m = cls.match(patterns[i])) {
            $('img.' + m[2]).show();
            $('h1.' + m[1]).show();
            break;
        }
    }
});

Here is a demo : http://jsfiddle.net/wared/UTe5F/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM