首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >我如何组织聚合物元素的结构,以拥抱聚合物开发定制元素的方式?

我如何组织聚合物元素的结构,以拥抱聚合物开发定制元素的方式?
EN

Code Review用户
提问于 2014-07-16 13:03:20
回答 1查看 2.1K关注 0票数 1

我的目标是学习Polymer,所以我开始开发一个复杂的组合框元素。

我在柱塞上做了我的开发。这是链接到我的扣子

我对我的.scss文件很满意,但我不会反对一些建设性的批评。话虽如此,我主要要找的是你对以下三个主题的看法:

  1. 我是否以糟糕的方式构造了我的combo-box.html模板绑定?如果是这样的话,我如何改进呢?我习惯于使用knockout.js,所以我的一些组织可能来自于我对它的经验。
  2. 目前,我正在使用属性来接受设置,combo-box的使用者可以传递给JavaScript逻辑。这是实现combo-box用户可以决定是否需要x而不是y特性的最佳方法吗?
  3. 最后,我想知道我是否能得到一些关于如何用combo-box.js组织代码的意见。我开始这种学习体验的想法是,除了polymer.js之外,我不想要任何外部依赖,当然,polymer.js所依赖的多填充的platform.js也不需要。尽管如此,我还是以这样一种基本方式构造了我的代码:(函数(高分子,未定义){ //这个闭包函数和函数// poly填充函数(这与我不使用其他libs的目标一样)/ fn返回一个对象(在我看来,这类似于我的模型/视图模型//调用聚合物函数和元素生命周期方法}( Polymer )的声明);

以下列举了三个要点:

combo-box.html

代码语言:javascript
运行
复制
<polymer-element name="combo-box">
    <template>
        <link rel="stylesheet"
            href="combo-box.css">
        <template bind="{{ comboBox }}">
            <div class="comboBoxWrap">
                <template bind="{{ elementAttributes }}">
                    <template if="{{ leftalignedicon }}">
                        <img src="{{ leftalignedicon }}"
                            class="leftAlignedIcon"
                            alt="leftAlignedIcon">
                        <input type="text"
                            class="leftAlignedIconInput"
                            value="{{ value }}"
                            on-focus="{{ toggleOptionsVisibility }}"
                            on-blur="{{ toggleOptionsVisibility }}"
                            on-keydown="{{ optionsInputKeydown }}">
                    </template>
                    <template if="{{ rightalignedicon }}">
                        <img src="{{ rightalignedicon }}"
                            class="rightAlignedIcon"
                            alt="rightAlignedIcon">
                        <input type="text"
                            class="rightAlignedIconInput"
                            value="{{ value }}"
                            on-focus="{{ toggleOptionsVisibility }}"
                            on-blur="{{ toggleOptionsVisibility }}"
                            on-keydown="{{ optionsInputKeydown }}">
                    </template>
                    <template if="{{ !leftalignedicon && !rightalignedicon }}">
                        <input type="text"
                            on-focus="{{ toggleOptionsVisibility }}"
                            on-blur="{{ toggleOptionsVisibility }}"
                            on-keydown="{{ optionsInputKeydown }}"
                            value="{{ value }}">
                    </template>
                </template>
                <template if="{{ showOptions }}">
                    <ul>
                        <template repeat="{{ opt in options }}">
                            <li class="{{ { active: opt.active } | tokenList }}"
                                on-mousedown="{{ optionClick }}"
                                on-mouseover="{{ onHovered }}" 
                                on-mouseout="{{ onUnhovered }}">{{ opt.text }}</li>
                        </template>
                    </ul>
                </template>
            </div>
        </template>
    </template>
    <script src="comboBox.js"></script>
</polymer-element>

combo-box.js

代码语言:javascript
运行
复制
(function(Polymer, undefined) {
    var pxSuffix = 'px';

    function getNodeIndex(node) {
        var index = -1;

        while ((node = node.previousSibling)) {
            if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
                index++;
            }
        }
        return index;
    }

    function getJSON(url, callback) {
        var request = new XMLHttpRequest();
        request.open('GET', url, true);

        request.onreadystatechange = function() {
            if (this.readyState === 4){
                if (this.status >= 200 && this.status < 400){
                    // Success!
                    callback(JSON.parse(this.responseText));
                }
                else {
                    // Error :(
                    callback(this);
                }
            }
        };

        request.send();
        request = null;
    }

    // constructor function for a new ComboBox
    function ComboBox(customElement) {
        var comboBoxModel = {
            value: '',
            options: [],
            showOptions: false,
            elementAttributes: {}
        };
        var i = 0;
        var currentAttr;
        var request;
        var historyprop = 'history';

        // take the html attributes from customElement
        // and augment them onto our comboBoxModel
        for (i; i < customElement.attributes.length; i++) {
           currentAttr = customElement.attributes[i];
           comboBoxModel.elementAttributes[currentAttr.nodeName] = currentAttr.nodeValue;
        }


        // get the data for our comboBox
        if(typeof comboBoxModel.elementAttributes.history !== 'undefined') {
            if(typeof comboBoxModel.elementAttributes.historyprop !== 'undefined') {
                historyprop = comboBoxModel.elementAttributes.historyprop;
            }
            getJSON(
                comboBoxModel.elementAttributes.history,
                function(responseObj) {
                    var iObj;
                    i = 0;
                    for (i; i < responseObj[historyprop].length; i++) {
                        iObj = responseObj[historyprop][i];
                        iObj.active = false;
                        comboBoxModel.options.push(iObj);
                    }
                }
            );
        }

        return comboBoxModel;
    }


    Polymer('combo-box', {
        created: function() {
            this.comboBox = ComboBox(this);
        },
        ready: function() {
            // I don't love this solution for icons in the input box, but this is what I got
            var shadowRoot = this.shadowRoot;
            var inputEl = shadowRoot.querySelector("input[type='text'");
            var imgEl = shadowRoot.querySelector('img');
            var imgHeight;
            var imgWidth;
            var inputHeight;
            var inputWidth;
            var heightDiffInHalf;

            if(inputEl !== null && imgEl !== null ) {
                imgHeight = imgEl.clientHeight;
                imgWidth = imgEl.clientWidth;
                inputHeight = inputEl.offsetHeight;
                inputWidth = inputEl.offsetWidth;
                heightDiffInHalf = (inputHeight - imgHeight) / 2;

                // set the img height to be the offsetTop of the input and center it within the input
                imgEl.style.top = inputEl.offsetTop + heightDiffInHalf + pxSuffix;

                // Depending on left vs right set the position of the img
                if(this.comboBox.elementAttributes.leftalignedicon || this.comboBox.elementAttributes.rightalignedicon) {
                    if(this.comboBox.elementAttributes.leftalignedicon) {
                        imgEl.style.left = inputEl.offsetLeft + pxSuffix;

                        inputEl.style.paddingLeft = imgWidth + pxSuffix;
                    }

                    if(this.comboBox.elementAttributes.rightalignedicon) {
                        imgEl.style.left = (inputEl.offsetLeft + inputWidth) + pxSuffix;
                        inputEl.style.paddingRight = imgWidth + pxSuffix;
                    }
                }
            }
        },
        attributeChanged: function(attrName, oldVal, newVal) {
            var _newVal = this.getAttribute(attrName);
            console.log('attributeChanged');
            console.log(attrName, 'old: ' + oldVal, 'newVal:', newVal);
            console.log('_newVal: ' + _newVal)
        },
        toggleOptionsVisibility: function(e) {
            // in the docs it says to get the model by doing:
            // var model = e.target.templateInstance.model;
            // BUT this seems to work
            // using self for setTimeout
            var self = this;

            self.comboBox.showOptions = !this.comboBox.showOptions;
        },
        optionsInputKeydown: function(e) {
            var keyCode = e.keyCode || e.which;
            var i = 0;
            var optionsLen = this.comboBox.options.length;
            var activeIndex = -1;

            // find the active option index
            for(i; i < optionsLen; i++) {
                if(this.comboBox.options[i].active === true){
                   activeIndex = i;
                   break;
                }
            }

            if(keyCode === 13) {
                // Enter
                console.log('Option chosen (Enter)... execute user defined callback');
                console.log(activeIndex);
            }
            else if(keyCode === 38) {
                // Up arrow
                if(activeIndex > 0 && activeIndex < optionsLen) {
                    this.comboBox.options[activeIndex].active = false;
                    this.comboBox.options[activeIndex - 1].active = true;
                }
                else {
                    if(activeIndex === 0) {
                        this.comboBox.options[activeIndex].active = false;
                    }
                    this.comboBox.options[optionsLen - 1].active = true;
                }
            }
            else if(keyCode === 40){
                // Down arrow
                if(activeIndex > -1 && activeIndex < (optionsLen - 1)) {
                    this.comboBox.options[activeIndex].active = false;
                    this.comboBox.options[activeIndex + 1].active = true;
                }
                else {
                    if(activeIndex === (optionsLen - 1)) {
                        this.comboBox.options[activeIndex].active = false;
                    }
                    this.comboBox.options[0].active = true;
                }
            }
        },
        onHovered: function(event, detail, sender) {
            var i = 0;
            var hoveredIndex = getNodeIndex(sender);

            for(i; i < this.comboBox.options.length; i++) {
                this.comboBox.options[i].active = false;
            }
            this.comboBox.options[hoveredIndex].active = true;
        },
        onUnhovered: function(event, detail, sender) {
            var hoveredIndex = getNodeIndex(sender);
            this.comboBox.options[hoveredIndex].active = false;
        },
        optionClick: function(event, detail, sender) {
            console.log('Option chosen (Click)... execute user defined callback');
            console.log(getNodeIndex(sender));
        }
    });


}(Polymer));

使用/配置

示例

代码语言:javascript
运行
复制
<!DOCTYPE html>
<html>
    <head>
        <title>combo-box</title>
    </head>
    <body>
        <h1>combo-box</h1>

        <!-- Polymer -->
        <script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.4/platform.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.4/polymer.js"></script>

        <!-- combo-box -->
        <link rel="import" href="combo-box.html">

        <!--
        Attribute Documentation:

        history: A path/url for an ajax request to return correctly formatted JSON
        historyProp: Property name that corresponds to the array of history options
            (Optional: Defaults to 'history')
        leftAlignedIcon: Path/url for an image to use as an icon in the left side of the input box
        rightAlignedIcon: Path/url for an image to use as an icon in the right side of the input box
        -->
        <combo-box history="history.json" historyProp="myHistory" ></combo-box>
        <combo-box history="history.json"
            historyProp="myHistory"
            leftAlignedIcon="https://cdn1.iconfinder.com/data/icons/google_jfk_icons_by_carlosjj/16/search.png">
        </combo-box>
        <combo-box history="history.json"
            historyProp="myHistory"
            rightAlignedIcon="https://cdn2.iconfinder.com/data/icons/snipicons/500/search-16.png">
        </combo-box>
    </body>
</html>
EN

回答 1

Code Review用户

回答已采纳

发布于 2014-07-16 15:51:31

只是想让你知道

代码语言:javascript
运行
复制
function getNodeIndex(node) {
    var index = -1;

    while ((node = node.previousSibling)) {
        if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
            index++;
        }
    }
    return index;
}

可能是

代码语言:javascript
运行
复制
function getNodeIndex(node) {
  return Array.prototype.indexOf.call(e.childNodes, someChildEl);
}

这适用于任何支持聚合物的浏览器

这似乎也很奇怪:

代码语言:javascript
运行
复制
    toggleOptionsVisibility: function(e) {
        // in the docs it says to get the model by doing:
        // var model = e.target.templateInstance.model;
        // BUT this seems to work
        // using self for setTimeout
        var self = this;

        self.comboBox.showOptions = !this.comboBox.showOptions;
    },

这句话过时了吗?这应该是简单的

代码语言:javascript
运行
复制
    toggleOptionsVisibility: function(e) {
        this.comboBox.showOptions = !this.comboBox.showOptions;
    },

// Up arrow// Down arrow之间存在一些严重的拷贝存储问题。

最后,从哲学的角度来看,如果combobox有一个包含活动项索引的activeIndex属性,那么您的代码可能会更精简。这样,您就不需要一直循环这些元素来激活/去激活/检查激活/检查去激活等等。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/57192

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档