2
\$\begingroup\$

I've got the following code which is ran inside tampermonkey (It's a userscript), but there is a variable that is being declared, used, then overwritten by the next line. I'm looking for a way to shorten this code, or possibly remove the line if I can get the same results another way.

$(function() {
        /***** RELATED TAGS *****/
        var oldtags = main_content.find('div#related-tags, .module.js-gps-related-tags');
        var vctags = genSBWidget('Related Tags', '<svg aria-hidden="true" class="svg-icon" width="18" height="18" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg>', 'relatedtags');

        oldtags.before(vctags).children('h4, a').remove().parent();

        vctags = main_content.find('div#visualcrumbs-relatedtags'); //THIS LINE

        vctags.prepend(oldtags.children()).children().wrapAll('<div class="js-watched-tag-list grid gs4 py4 fw-wrap">').each(function() {
            $(this).toggleClass('dno js-hidden js-tag grid--cell').children().slice(1).remove();
        });
});

genSBWidget simply generates some HTML, not sure if this is really relevant but I'll post the function anyways:

function genSBWidget(title, icon, innerid) {
        var widget = '\
        <div class="s-sidebarwidget" style="margin-bottom:19.500px;">\
            <div class="s-sidebarwidget--header grid">\
                 <span class="grid--cell mr4">'+ icon +'</span>\
                 <span class="grid--cell fl1">' + title + '</span>\
            </div>\
            <div id="visualcrumbs-'+ innerid +'" class="s-sidebarwidget--content fd-column"></div>\
        </div>\
        ';
        return widget;
    }

Side not: This script just takes "Related tags" in the right sidebar of stackoverflow and puts it into one of the new style boxes in the sidebar, and gives it the same styling as Watched Tags.

My actual userscript works on more than just related tags, it converts everything in the sidebar into this new style box, what I'm hoping to do from this question is learn some new methods of doing what I'm trying to do with related tags, so I can expand this accross my entire script.

Here is a minimal example of the script:

// ==UserScript==
// @name         Related Tags Beautifier
// @namespace    https://github.com/GrumpyCrouton/Userscripts
// @version      1.0
// @description  Customizes StackExchange to your liking.
// @author       GrumpyCrouton
// @match          *://*.stackoverflow.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var $ = window.jQuery;

    /***** GLOBAL SITE CHANGES *****/
    var main_content = $('body div.container').find('div#content');

    $(function() {
        /***** RELATED TAGS *****/
        var oldtags = main_content.find('div#related-tags, .module.js-gps-related-tags');
        var vctags = genSBWidget('Related Tags', '<svg aria-hidden="true" class="svg-icon" width="18" height="18" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg>', 'relatedtags');

        oldtags.before(vctags).children('h4, a').remove().parent();
        vctags = main_content.find('div#visualcrumbs-relatedtags');
        vctags.prepend(oldtags.children()).children().wrapAll('<div class="js-watched-tag-list grid gs4 py4 fw-wrap">').each(function() {
            $(this).toggleClass('dno js-hidden js-tag grid--cell').children().slice(1).remove();
        });

     });

    function genSBWidget(title, icon, innerid) {
        var widget = '\
        <div class="s-sidebarwidget" style="margin-bottom:19.500px;">\
            <div class="s-sidebarwidget--header grid">\
                 <span class="grid--cell mr4">'+ icon +'</span>\
                 <span class="grid--cell fl1">' + title + '</span>\
            </div>\
            <div id="visualcrumbs-'+ innerid +'" class="s-sidebarwidget--content fd-column"></div>\
        </div>\
        ';
        return widget;
    }

})();
\$\endgroup\$

1 Answer 1

2
+25
\$\begingroup\$

[...], but there is a variable that is being declared, used, then overwritten by the next line.

Indeed. And it's not clear why it is that way. I believe you're talking about vctags, here:

vctags = main_content.find('div#visualcrumbs-relatedtags'); //THIS LINE

vctags.prepend(oldtags.children()).children().wrapAll('<div class="js-watched-tag-list grid gs4 py4 fw-wrap">').each(function() {
    $(this).toggleClass('dno js-hidden js-tag grid--cell').children().slice(1).remove();
});

The assignment here is unnecessary, because you could chain the call directly on main_content.find(...), without assigning the result to a variable. It would be quite natural that way.

I find long chains easier to read when split to multiple lines, like this:

main_content
    .find('div#visualcrumbs-relatedtags')
    .prepend(oldtags.children())
    .children()
    .wrapAll('<div class="js-watched-tag-list grid gs4 py4 fw-wrap">')
    .each(function() {
        $(this).toggleClass('dno js-hidden js-tag grid--cell').children().slice(1).remove();
    });

Unnecessary selector

The last .parent() here is unnecessary, you can safely drop it:

oldtags.before(vctags).children('h4, a').remove().parent();

Generating the new widget

The value of the second parameter is too long, and it's hard to read what else is on the line:

var vctags = genSBWidget('Related Tags', '<svg aria-hidden="true" class="svg-icon" width="18" height="18" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg>', 'relatedtags');

It would be better to put that value in a variable:

var icon = '<svg aria-hidden="true" class="svg-icon" width="18" height="18" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg>';
var vctags = genSBWidget('Related Tags', icon, 'relatedtags');

Like this, it's reasonably clear that the line var icon = '...' is just a very long string and so even if I don't scroll to the far right, I probably won't miss anything too important. And the parameters of the function call are now clearly visible.

Furthermore, in the posted code genSBWidget is only called once. If that's the case in your real code, then there's no need for the parameters, they could be inlined in the function itself.

Naming

I suggest some renames:

  • oldtags to oldWidget and vctags to newWidget, as I think that describes better what they really are.
  • main_content to mainContent to make the naming style consistently camelCase.

Alternative implementation

Putting the above together, I would write the main function this way:

$(function() {
    var icon = '<svg aria-hidden="true" class="svg-icon" width="18" height="18" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg>';
    var newWidget = genSBWidget('Related Tags', icon, 'relatedtags');
    var oldWidget = mainContent.find('div#related-tags, .module.js-gps-related-tags');

    oldWidget.children('h4, a').remove();
    oldWidget.before(newWidget);

    mainContent
        .find('div#visualcrumbs-relatedtags')
        .prepend(oldWidget.children())
        .children()
        .wrapAll('<div class="js-watched-tag-list grid gs4 py4 fw-wrap">')
        .each(function() {
            $(this).toggleClass('dno js-hidden js-tag grid--cell').children().slice(1).remove();
        });
});

Note that I did not chain the calls on oldWidget, even though I could have written as:

    oldWidget
        .before(newWidget)
        .children('h4, a')
        .remove();

I think the way I wrote above is somewhat easier to read, clear and unambiguous without much thinking.

\$\endgroup\$
6
  • \$\begingroup\$ Actually I'm not talking about the prepend part of my code, I'm talking about how vctags is defined, and then a few lines later it is defined again (and overridden) where I put the //this line comment. This was a very early version of what I was doing, and you can see the current version here: pastebin.com/s22UtRsx - which I think fixed a couple of the things you have suggested here. Thank you for your answer, I did learn a few things. \$\endgroup\$ Aug 13, 2018 at 19:08
  • \$\begingroup\$ @GrumpyCrouton I don't know how to make that more clear, but I'm talking exactly about the line vctags = main_content.find(...); //THIS LINE. Overwriting vctags like this is pointless. You don't need to store the result of the main_content.find(...) so there's no need to assign it to vctags or to any other variable. If you really really want to assign it somewhere, then you could always define a new variable for that. In this example I don't see a reason to do that. \$\endgroup\$
    – janos
    Aug 13, 2018 at 19:15
  • \$\begingroup\$ @GrumpyCrouton Another example. let x = main_content.find(...); x.prepend(...) is exactly the same as main_content.find(...).prepend(...), but without the unnecessary assignment and the unnecessary variable x. So the line in your code marked with //THIS LINE, if combined with the next line by chaining the code, the vctags = can be safely deleted without changing the meaning of the program. I hope you see my point, or that you can explain what I'm missing. \$\endgroup\$
    – janos
    Aug 13, 2018 at 20:38
  • \$\begingroup\$ I understand that concept, but in my current code that would mean writing main_content.find(...) twice, so it would have more code repetition and at that point I'd rather just have the extra line of code. \$\endgroup\$ Aug 13, 2018 at 20:43
  • \$\begingroup\$ @GrumpyCrouton In the code posted in the question, two main_content.find(...) calls cannot be avoided, because they select two different objects in the DOM. Those two calls are fine like that, nothing wrong with them. If by "current code" you're referring to code outside the question, that's not appropriate to address in this question-answer thread. On this site, the code to review must be inside the question, not on a 3rd party site. \$\endgroup\$
    – janos
    Aug 13, 2018 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.