2

不久前我开始使用 JQuery 来做一些小事情,但是 JQuery 越来越多地成为整个工作流程的一部分,并且开始变得混乱:

UI 消息遍布整个代码,悬停在不同位置的图像按钮,以及其他一些代码异味。像这样的事情:

$("#button").hover(function(){
        $(this).attr("src",ROOT_PATH + "images/button_01b.png");
        $(this).css("margin", "-4px 0 0 0");
    }, function(){
        $(this).attr("src",ROOT_PATH + "images/button_01.png");
        $(this).css("margin", "0 0 0 0");
    });

当然,当我想测试不同的按钮时,我必须搜索按钮路径的每个实例并替换它。消息的类似问题。

我在保持一些配置变量与服务器代码同步方面也存在问题:我使用 PHP 中的配置文件来设置一些站点范围的设置,例如根文件夹的路径和访问者的语言。

在 PHP 中,我只需调用一个配置文件,它会找出正确的路径和语言,但 JQuery 文件无法访问这些变量。

我可以想到一些解决方案,比如将 PHP 中的变量输出到 HTML 隐藏的 div 中,然后用 JQuery 读取这些 div 的内容,但感觉就像一个糟糕的 hack,而且我想这个问题已经解决了很多次。

处理这个问题的标准解决方案是什么?

4

1 回答 1

3

在查看了您的代码后,我已经调整并重组了我的答案。我尝试根据您的需求对其进行定制,同时尽可能保持通用性,以便其他观众将来也可以使用它。

站点范围的服务器配置

先说易事。对于我的站点范围的服务器共享配置,我通常使用如下内容:

<script>
<!--[CDATA[
    var ROOT_URL = "<?php echo $config["root_url"] ?>";
]]-->
</script>

这当然只对不需要频繁更新的简单配置有用。

一种更优雅的方法来检索站点范围的配置,甚至允许更复杂的值 - 例如包含双引号的值 - 对于json_encode它们来说,让 jQuery 再次解码 JSON。这也将允许您轻松更新这些配置。为了避免将 JSON 字符串括在更多的双引号中导致可能的中断,为什么不利用浏览器通常不解析<script>标签之间的任何内容这一事实呢?

<script type="text/json" id="config">
    <?php echo json_encode($config) ?>
</script>

完全有效,因为它不会被 JavaScript 引擎解释并且通常被浏览器忽略。然后您的脚本可以像这样访问它:

var config = $.parseJSON($('#config').html());

我在学习使用 WebGL 时学到了这个小技巧。尽管通常建议将脚本内容包装在CDATA中,但在这种情况下,提取内容会更加困难,我认为它相当过时,tbh。

正确使用 jQuery

你显然不知道 jQuery 的真正威力。像这样的电话

$('#status').html("<h2>Thanks! We will get back to you as soon as possible.</h2>")
.css('margin', '-300px 0 0 250px')
.css('background', '#C9DDFC')
.css('width', '500px')
.css('border', '2px solid black')
.css('position', 'relative')
.css('z-index', '100')
;

function userNameOk( userName ) {
    var queryType   = "checkUsrName";
    var usrName     = userName;
    var usrNameOk   = false;
    $.ajax
    ({
        async: false,
        url: ROOT_PATH +'handlers/buyer_data_handler.php',
        type: 'POST',
        data: 'usrName=' + usrName + '&queryType=' + queryType,

        success: function(result)
        {
            var obj = jQuery.parseJSON(result);
            if (obj.usrNameOk == "yes")
            {
                usrNameOk = true;
            }
            else
            {
                usrNameOk = false;
                alert(obj.msg);
            }
        }
    });
    return usrNameOk;
}

可以像这样简单地完成:

$('#status').html("Bla").css({
    margin : '-300px 0 0 250px',
    background : '#c9ddfc',
    width : 500, // jQuery doesn't even need 'px' for some styles! But don't stringify them...
    border : '2px solid black',
    position : 'relative',
    zIndex : 100 // Camel case for attributes with dashes.
});

function userNameOk( userName ) {
    var usrNameOk;
    $.ajax({
        async : false,
        url : ROOT_PATH + 'handlers/buyer_data_handler.php',
        type : 'POST',
        data : {
            usrName : userName,
            queryType : 'checkUsrName'
        },
        dataType : 'json',

        success : function ( result ) {
            usrNameOk = result.usrNameOk == 'yes'; // !! makes sure you get a boolean value.
            if( !usrNameOk ) {
                alert(result.msg);
            }
        }
    });
}

请注意,它jQuery.ajax接受一个普通的 JS 对象作为data参数,还允许您指定预期的服务器响应类型。如果您告诉 jQuery 您需要 JSON,它会自动将其解析为一个对象——除非它格式错误,但在这种情况下,您的方法也会出错。

如果我不说明这一点,社区会。但通常不鼓励使用同步 AJAX 请求——A 代表 Asynchronous 首先——因为它会阻止网站执行任何其他操作,直到它完成。如果您的服务器无法满足所有请求,或者他们自己的连接速度非常慢,您的听众将不会同意。事实上,起初他们可能甚至不会意识到这是一个导致麻烦的请求,因为标准用户不知道幕后发生了什么。

最好使用“加载图像” - 我认为同步请求甚至会阻止该动画 - 以及您的回调。这就是它的主要用途。

这些只是jQuery 使您能够做什么的极少数示例。花时间学习如何使用 jQuery 确实是值得的。

结构化你的代码

将重复算法(例如弹出模式框或定义的动画序列)转移到一些附加功能。如果您有一个非常长的函数,这将特别有用。

尽管该功能的某些部分可能只需要一次,但将其拆分为几个子功能仍然有很大帮助。在一个更大的函数中调用几个小函数比一个几十行代码的大函数更容易理解。

拥有模块化开发代码甚至可能是一个好主意 - 即在您的测试站点上加载多个 JS 文件,但在部署时,合并它们并使用压缩器。

构建代码的最后一个重要提示:泛化它!

虽然这是您的功能的开始:

$("#regError").html(""); // reset error message
var referer = $("#referer").val();
var clientIP = $("#clientIP").val();
var lastName = $("#lastName").val();
var firstName = $("#firstName").val();
var username =  $("#newUsrName").val();
var password =  $("#usrPass").val();
var confirmPass = $("#confirmUsrPass").val();
var email = $("#usrEmail").val();
var website = $("#usrWebsite").val();

var errors = "";
var error_msg = "";

if (userNameOk(username))
{
    $("#newUsrName").css("border", "2px solid green");
    $("#available").html(" OK");
}
else
{
    $("#newUsrName").css("border", "2px solid red"); 
    $("#newUsrName").focus();            
}
if (lastName.length <3)
{
    $("#lastName").css("border", "2px solid red");
    errors++;
}
else
{
    $("#lastName").css("border", "2px solid green");
}
if (firstName.length <3)
{
    $("#firstName").css("border", "2px solid red");
    errors++;
}
else
{
    $("#firstName").css("border", "2px solid green");
}
if (password.length <6)
{
    $("#usrPass").css("border", "2px solid red");
    $("#confirmUsrPass").css("border", "2px solid red");
    error_msg += "The password must be at least 6 characters long<br>";
    errors++;
}
else if (confirmPass != password)
{
    $("#usrPass").css("border", "2px solid red");
    $("#confirmUsrPass").css("border", "2px solid red");
    error_msg += "The confirmation password doesn't match the password.<br>";
    errors++;
}
else
{
    $("#usrPass").css("border", "2px solid green");
    $("#confirmUsrPass").css("border", "2px solid green");
}
if (!isValidEmail(email))
{
    $("#usrEmail").css("border", "2px solid red");
    error_msg += "A valid email is needed to validate your registration.<br>";
    errors++;
}
else
{
    $("#usrEmail").css("border", "2px solid green");
}

if (errors > 0)
{
    $("#regError").html(error_msg).css("color", "red");
    return false;
}

它不仅适用于其他结构化技巧,而且适用于泛化。这显然是一种验证算法。首先,我想我应该让你知道 HTML5 规范定义了浏览器原生验证实现。因此,通过使用 HTML5 属性,在许多情况下,您可以省去几乎所有这些代码行......

在 W3Schools 上查看表单属性!

然而,并不是每个浏览器都已经实现了这个功能。因此,使用Modernizr来检测浏览器支持并在必要时延迟加载插件确实是有意义的。

然后,您的代码可能看起来像这样:

function validate( form ) {
    var valid = true;

    $('input[required][name], textarea[required][name]', form).each(function( index, elem ) {
        var $elem = $(elem),
            val = $elem.val(),
            pattern = $elem.attr('pattern').
            regex = null;

        // Validation pattern provided?
        if( typeof pattern !== 'undefined' && pattern !== false ) {
            regex = new RegExp(pattern);
        }

        // Validation through pattern
        // For minimum length, see https://stackoverflow.com/questions/10281962/is-it-minlength-in-html5
        if( regex ) {
            if( !regex.match(val) ) {
                valid = false;
            }
        }
        else {
            if( val.trim().length === 0 ) {
                valid = false;
            }
        }

        if( !valid ) {
            notifyRequired(elem);
        }
    });

    return valid;
}

这只是一个简单的例子,但可以在不同的地方使用——即你的所有表格。这个小脚本使用了一些 HTML5 属性,尽管它们可能还不被支持。哦,当我在做的时候,看看这个问题

事实上,像这样的通用代码可以用于您在所有网站项目中共享的某种核心库中,从而避免您重复工作。

编辑:既然你问了,实际上通过例如使用事件侦听器来调整对自定义处理的支持是相当容易的。我写了一个简单的 jsFiddle来演示。

每个事件对象都接受一个返回值,包括本机事件。jQuery 的 Event 对象提供了result自动返回最后一个事件监听器的值的属性。如果未定义,只需进行标准测试。否则以某种方式使用结果。如果需要,我的示例只是设置valid为 false。

上述validate函数中包含的最相关部分如下所示:

var jqEvt = $.Event('validate');

$(this).trigger(jqEvt);
if( typeof jqEvt.result !== 'undefined' ) {
    if( !jqEvt.result ) {
        valid = false;
    }
}
else {
    // All the other stuff...
}

我们必须显式使用工厂或构造函数才能使用处理程序的结果值。

在函数的开头,声明所有变量。这不仅有助于调试代码。由于 JS 的特性,当不处于严格模式时,无论如何都会使用所有未声明的变量。但是,当您稍后在代码中“重新声明”它们时,它们将被重新初始化为 value undefined。如果您还不知道,请阅读一些关于undefined- 它非常特别。

这在暂停代码开发或协作时特别有用,因为它会向您显示哪些变量不要在代码中进一步使用。

不需要用 声明每个变量var。您可以用 . 分隔声明,

// Change all this...

var title = $('#select_title').val();
var lastName = $('#lastName').val();
var firstName = $('#firstName').val();
var company = $('#company').val();
var email = $('#email').val();
var phone = $('#phone').val();
var fullName = $('#fullName').val();
var instructions = $("#comments").val();
var QuoteData = "SourceLang=" + source_lang +
                "&TargetLangs=" + target_langs +
                "&Wordcount=" + wordcount +
                "&FilesToTranslate=" + filesToTranslate +
                "&title=" + title + 
                "&lastName=" + lastName + 
                "&firstName=" + firstName +
                "&Company=" + company +
                "&Email=" + email +
                "&Phone=" + phone + 
                "&fullName=" + fullName +
                "&instructions=" + instructions;

// to that:

var title = $('#select_title').val(),
    lastName = $('#lastName').val(),
    firstName = $('#firstName').val(),
    company = $('#company').val(),
    email = $('#email').val(),
    phone = $('#phone').val(),
    fullName = $('#fullName').val(),
    instructions = $("#comments").val(),

    QuoteData = "SourceLang=" + source_lang +
                "&TargetLangs=" + target_langs +
                "&Wordcount=" + wordcount +
                "&FilesToTranslate=" + filesToTranslate +
                "&title=" + title + 
                "&lastName=" + lastName + 
                "&firstName=" + firstName +
                "&Company=" + company +
                "&Email=" + email +
                "&Phone=" + phone + 
                "&fullName=" + fullName +
                "&instructions=" + instructions;

我发现即使删除那些额外var的 s 也会使代码稍微干净一些。这也取决于上面的提示。由于这都是通过请求发送到服务器的数据的设置,因此您也可以简单地执行以下操作:

$.ajax({
    url : 'foo.bar',
    data : {
        SourceLang : source_lang,
        TargetLangs : target_langs,
        Wordcount : wordcount,
        FilesToTranslate : filesToTranslate,
        // ... and so forth
    }
});

事实上,您甚至可以编写另一个生成名称的函数:表单的所有输入元素的值映射。像这样的东西,它又可以以不同的形式使用。

function getFields( form ) {
    var result = {};
    // Short hand for $(form).find('input textarea')
    $('input[name] textarea[name]', form).each(function(index, elem) {
        var name = $(elem).attr('name'),
            val = $(elem).val(),
            tmp;
        if( name.indexOf('[]') > -1 ) {
            name = name.substring(0, name.indexOf('[]'));
            if( name in result ) {
                result[name].push(val);
            }
            else {
                result[name] = [val];
            }
        }
        else {
            result[name] = val;
        }
    });
    return result;
}

将相互关联的代码行用空行分组并注释。让您先了解代码的作用,然后再继续 - 再次。评论不仅在协作时有用,对您自己也有用。

还要注意不要在前两个函数中使用不必要的变量。需要理解的代码越少越好。

function login( userName, password ) {
    var usrName = userName; // <-- totally
    var pass    = password; // <-- redundant
}

在旁注中,请始终记住:尽可能全局化,尽可能局部化。尽管这对于 Java 和 C++ 等其他编程语言更为重要,但它可以解释为什么包括我在内的许多 JS 开发人员喜欢将他们的整个代码包含在一个匿名函数中,如下所示:

(function(w, d, $){
    // ... your code
})(window, document, jQuery);

我听说过使用wandd而不是windowand之document类的一些特殊好处,但我不太记得了。但是,jQuery这样使用确实有一个显着的优势:如果您碰巧使用了另一个库覆盖,您仍然可以在不干扰其他代码的情况下$引用它。jQuery例如,这在开发 Mootools 附带的 Joomla 前端时非常有用。


以下提示是有偏见的,旨在帮助维护大型项目。

Google 闭包编译器

在我看来,一个相当专业的工具是Google Closure Compiler,尽管我怀疑它的缩写是 GNU Compiler Compilation 的缩写。它与清理代码的关系不大,但有助于保持对大型项目的清晰概述。

Closure Compiler 不仅可以压缩 JavaScript,还可以将不同的 JavaScript 文件编译成一个文件。您可以在三种不同的模式下操作 Closure Compiler。如果不在高级模式下运行,我只会将它用于自动将多个源文件压缩为一个。

我将简要解释为什么我喜欢在高级模式下使用 Closure Compiler 来处理大型项目。

  1. 如果您的 IDE 不这样做,它会在源文件中出现错误时告诉您。
  2. 它巧妙地缩小脚本:在缩小版本中自动忽略未使用的代码。
  3. 它使用注释!这些注释可用于进一步定义编译器的行为。

关于注释,闭包编译器允许标记对象,就enum好像值是原始值或字符串一样。标记的变量const不会是缩小代码中的变量。对该特定常量的每个引用都将替换为效率的实际值。它提供了很多很多有用的注解,比如@protected只允许同一文件中的其他变量和方法访问这个特定变量或方法的注解,类似于 Java 中的“包可访问性”。

现在使用 Google Closure Compiler 真的不是一件容易的事,我真的不推荐它用于较小的项目,因为有更好的解决方案。做起来可能很乏味,尤其是试图让它做你想让它做的事情。但是一旦你正确配置它,它就真的派上用场了。

SCSS/SASS/LESS

这三种语言都是 CSS 语言的变体,允许您编写清晰整洁的 CSS 方言,并将其编译为(希望)有效的 CSS。虽然SCSSLESS非常相似,但 SASS 是 SCSS 的另一种方言。但是,我使用 LESS,因为 SCSS 和 SASS 需要安装 Ruby——而我根本不希望这样。LESS 的另一个不错的特性是,如果检测到任何更改,它会自动编译样式表并将结果输出到指定的文件。

所有这些方言都允许一些漂亮的“编程”。它们在一定程度上允许不同的附加程序元素。这些元素将被计算并静态包含在文档中。90% - 5px只有当父元素之一设置了静态宽度时,才会(也许没有尝试过)之类的东西才有效。但据我了解,单位必须匹配。5em - 5px因此是不可能的。

但是,我之所以真正推荐这些“方言”是因为选择器嵌套。在 CSS 中看起来像这样的东西

#content .buttons .button {
    background: green;
}
#content .buttons .button.active {
    background: pink;
}
#content .buttons .button.active:hover {
    background: purple;
}

可以写成

#content .buttons .button {
    background: green;

    &.active {
        background: pink;
    }
    &.active:hover {
        background: purple;
    }
}

where&引用您当前所在的选择器。这不仅可能缩短您的 CSS - 或者更确切地说是 SCSS/SASS/LESS - 而且还使其更易于理解和维护。

完毕!

嗯,不完全是。这只是您真正可以做的事情的一部分,不仅可以编写更简洁而且更高效的代码。如果我把这一切都写下来,我实际上可以举办一个研讨会并为此要钱。而且由于我没有得到报酬并且已经为此花费了两个多小时,我认为我绝对应该结束这一切。尽管如此,我还是得照顾好自己的东西。

于 2013-05-22T15:09:11.310 回答