我的目标是学习Polymer,所以我开始开发一个复杂的组合框元素。
我在柱塞上做了我的开发。这是链接到我的扣子。
我对我的.scss文件很满意,但我不会反对一些建设性的批评。话虽如此,我主要要找的是你对以下三个主题的看法:
combo-box.html模板绑定?如果是这样的话,我如何改进呢?我习惯于使用knockout.js,所以我的一些组织可能来自于我对它的经验。combo-box的使用者可以传递给JavaScript逻辑。这是实现combo-box用户可以决定是否需要x而不是y特性的最佳方法吗?combo-box.js组织代码的意见。我开始这种学习体验的想法是,除了polymer.js之外,我不想要任何外部依赖,当然,polymer.js所依赖的多填充的platform.js也不需要。尽管如此,我还是以这样一种基本方式构造了我的代码:(函数(高分子,未定义){ //这个闭包函数和函数// poly填充函数(这与我不使用其他libs的目标一样)/ fn返回一个对象(在我看来,这类似于我的模型/视图模型//调用聚合物函数和元素生命周期方法}( Polymer )的声明);以下列举了三个要点:
<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>(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));示例
<!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>发布于 2014-07-16 15:51:31
只是想让你知道
function getNodeIndex(node) {
var index = -1;
while ((node = node.previousSibling)) {
if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
index++;
}
}
return index;
}function getNodeIndex(node) {
return Array.prototype.indexOf.call(e.childNodes, someChildEl);
}这适用于任何支持聚合物的浏览器。
这似乎也很奇怪:
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;
},这句话过时了吗?这应该是简单的
toggleOptionsVisibility: function(e) {
this.comboBox.showOptions = !this.comboBox.showOptions;
},在// Up arrow和// Down arrow之间存在一些严重的拷贝存储问题。
最后,从哲学的角度来看,如果combobox有一个包含活动项索引的activeIndex属性,那么您的代码可能会更精简。这样,您就不需要一直循环这些元素来激活/去激活/检查激活/检查去激活等等。
https://codereview.stackexchange.com/questions/57192
复制相似问题