Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
面是承接上文的知識點簡介:
6.表單
對于功能性的表單,HTML僅僅是產生表單的表面樣子,為后臺提供數據。
創建所有表單 <form></form>
創建一個滾動菜單,size設置在需要滾動前可以看到的表單項數目 <select multiple name=”NAME” size=</select>
設置每個表單項的內容 <option>
創建一個下拉菜單 <select name=”NAME”></select>
文本框區域,列的數目設置寬度,行的數目設置高度 <textarea name=”NAME” cols=40 rows=8></textarea>
復選框,文字在標簽后面 <input type=”checkbox” name=”NAME”>
單選框,文字在標簽后面 <input type=”radio” name=”NAME” value=”x”>
單行文本輸入區域,size設置以字符計的寬度 <input type=text name=”foo” size=20>
submit(提交)按鈕 <input type=”submit” value=”NAME”>
使用圖象的submit(提交)按鈕 <input type=”image” border=0 name=”NAME” src=”name.gif”>
reset(重置)按鈕 <input type=”reset”>
本來以為后面還會有更多知識點可以分享給大家,誰知都是一下雜亂無章的筆記而已
下面我想和大家分享一下,CSS的選擇器吧
文章如下
在CSS中,選擇器用于選擇需要添加樣式的元素,簡單的講,就是確定你要把樣式給那個元素。在我們寫web前端的時候,選擇器的使用是非常頻繁的,合理的使用選擇器會精簡大量的代碼。小松果總結了下,CSS選擇器大致的歸類為基礎選擇器和符合選擇器兩類。
1、基礎選擇器
a、html標簽選擇器:
標簽{屬性:屬性值}
div、p、h1、span……
b、class 類選擇器,可以共用重復:
.name {屬性:屬性值}
標簽中有class=”name”的元素
c、id選擇器: 唯一性
#name{屬性:屬性值}
標簽中有id=”name”的元素
d、屬性選擇器
*[title] {屬性:屬性值}
所有包含標題(title)的元素
e、通配符選擇器,所有的元素:
* {屬性:屬性值}
例:* {margin:0; padding:0}
2、復合選擇器
a、 后代選擇器:
A B { 屬性:屬性值}
注:A指父級選擇器 B指子級選擇器
b、偽類選擇器:
a:link {屬性:屬性值} 默認
a:visited {屬性:屬性值} 訪問過的鏈接
a:hover {屬性:屬性值} 鼠標經過
a:active {屬性:屬性值} 激活狀態(選中時的狀態)
注:順序不能錯 l v h a,還有一個a {屬性:屬性值} ,這個也要寫,里面放超鏈接中相同屬性,不同屬性分別寫在不同的偽類選擇器中。
信大家看到這張圖片就知道我們這篇文章要講什么了,沒錯就是-商品多規格選擇的解法。
近來在掘金上面看見大家都在研究“商品多規格選擇”的問題,例如晨曦大佬的前端電商 sku 的全排列算法很難嗎?學會這個套路,徹底掌握排列組合。 在這篇文章里面,大佬寫明了如何實現sku的全排列,思路非常的棒,但是并沒有緊貼業務場景。真正的業務場景是,我們要根據用戶每一次選擇的規格,找出剩下可選的規格和不可選的規格,表現在前端頁面上:就是將不可選的規格置回,也就是如下效果(可以點擊這里查看最終效果):
那么今天我們就來講講這個問題的一個解決方法,要講明白很難,但是我相信你看了這篇文章之后,sku就再也難不倒你了。
在介紹具體解法之前,我們先來介紹一下什么是sku? sku是會計學中的一個名詞,被稱作庫存單元。說人話?簡單來講就是,我們上圖 中每一個單規格選項,例如深空灰色、64G,都是一個規格(sku)。商品和sku屬于一對多的關系,也就是我們可以選擇多個sku來確定到某個具體的商品:
可以這么說,只要是做電商類相關的產品,比如購物 APP、購物網站等等,都會遇到這么一個場景,每個商品對應著多個規格,用戶可以根據不同的規格組合,選擇出自己想要的產品。我們自己在生活中也會經常用到這個功能,然而就是這樣一個簡單的功能,卻難倒了很多小伙伴。
筆者也是一樣,剛開始遇到這個場景,筆者覺得應該一個下午就能搞定,完美收工,奈何還是太過于年輕,搞了差不多兩天,在網上查閱了很多相關的文章和資料,但是不得其解,最后沒有辦法,只能硬著頭皮采用暴力求解(也就是不斷循環)的方法來解決的,時間復雜度賊高,達到了O(m*n)也就是O(n2),這種實現方法其實也不是不行(能跑就行),對吧。但是后來筆者發現,當一個商品的規格非常非常多、并且用戶的設備性能不是那么好的情況下,那么這種實現方式就會導致運行時間過長,表現在頁面上就是:當用戶點擊了一個規格,會有明顯的卡頓,那怎么行,客戶都流失了,老板還怎么買法拉利 ??所以筆者又開始了研究。
一個偶然的機會,筆者在逛知乎的時候,看到了有人在討論圖,這個數據結構,突然靈光一現,貌似咱們的多規格選擇也可以用圖來作求解方法,后來一嘗試,還真的可行。而且時間復雜度只有O(n),簡直完美。所以我們下面來介紹一下圖,什么是圖?相信大學學過數據結構與算法的同學都應該知道,不過應該已經忘得一干二凈了。
圖其實是數學的一個分支。它以圖為研究對象。圖論中的圖是由若干給定的點及連接兩點的線所構成的圖形,這種圖形通常用來描述某些事物之間的某種特定關系,用點代表事物,用連接兩點的線表示相應兩個事物間具有這種關系:
圖通常有如下分類:
好了知道這兩個概念就差不多了,當然如果想了解更多更多概念,請看這里
那么我們需要用到的是無向圖,什么是無向圖呢,就像這樣:
兩個頂點之間如果有連線,則表示這兩個頂點是互通的。小伙伴們看到這里可能會懵逼了,說了這么多,好像跟我們要解決的問題沒關系啊。小伙伴們現在想一想:用戶在選擇規格的時候,肯定是沒有先后順序的,假設我們現在把每種規格看作是無向圖的一個頂點的話,我們可以根據這些單項規格的組合規格,就可以畫出一個像上圖一樣的無向圖。
假設我們已經畫出了如上 的無向圖,那么我們如何將這個圖用咱們的代碼來表示呢?這里就用到了鄰接矩陣
鄰接矩陣其實是《線性代數》里面的概念,相信很多小伙伴都不會陌生,我們在代碼中,表示它的方法是用一個n x n的二維數組來抽象鄰接矩陣。讓我們來把上面 這個無向圖用鄰接矩陣(二維數組)表示出來:
很顯然,如果兩個頂點互通(有連線),那么它們對應下標的值則為 1,否則為 0。
好了,下面開始逐步都是高能,請小伙伴們認真觀看。
假設現在我們有如下規格列表:
specList: [
{ title: "顏色", list: ["紅色", "紫色"] },
{ title: "套餐", list: ["套餐一", "套餐二"] },
{ title: "內存", list: ["64G", "128G", "256G"] },
];
可供選擇的規格組合有:
specCombinationList: [
{ id: "1", specs: ["紫色", "套餐一", "64G"] },
{ id: "2", specs: ["紫色", "套餐一", "128G"] },
{ id: "3", specs: ["紫色", "套餐二", "128G"] },
{ id: "4", specs: ["紅色", "套餐二", "256G"] }
],
首先,我們根據specList知道:我們有“顏色”、“套餐”、“內存”三種規格類別。分別有紅色、紫色、套餐一、套餐二、64G、128G、256G這些單項規格。每個單項規格作為一個頂點,所以就有如下頂點:
然后我們根據specCombinationList,我們可以知道,哪些規格的組合是可選的。好了我們要開始畫圖了。
根據{ id: "1", specs: ["紫色", "套餐一", "64G"] },我們可以畫出:
接下來依葫蘆畫瓢:我們可以根據specCombinationList剩下的數據畫出如下的圖:
好了,我們已經根據specCombinationList(也就是可選規格組合)將我們的規格無向圖畫完了。現在我們來模擬一下用戶的選擇:
specCombinationList: [
{ id: "1", specs: ["紫色", "套餐一", "64G"] },
{ id: "2", specs: ["紫色", "套餐一", "128G"] },
{ id: "3", specs: ["紫色", "套餐二", "128G"] },
{ id: "4", specs: ["紅色", "套餐二", "256G"] }
],
假設用戶先選擇了紫色、根據specCombinationList,我們發現套餐一、套餐二、64G、128G是可選的,這個時候我們發現一個問題:顯然跟紫色同級的紅色其實也是可選的。所以這個圖其實我們還沒有畫完。所以相同類型的規格其實是應該連接起來的:
好了,無向圖畫好了,現在我們將它映射到鄰接矩陣上面(這一步強烈建議小伙伴們拿出紙筆來一起畫一畫):
到了這一步,恭喜你,你已經懂了一大半了 。
好了,到這我們就可以公布最終結論了:
說真的,我覺得小伙伴們看明白了我上面 這些講解,相信你已經完全懂了該如何實現“多規格選擇”算法了。不過有句話叫做:光說不練假把式!那下面我們就一起來捋一捋,用代碼如何實現吧,筆者這里用的前端框架是react,明白思路了,用什么框架都一樣的哦。
這里先說下思路:
1、根據規格列表(specList)創建鄰接矩陣(數組)
2、根據可選規格組合(specCombinationList)填寫頂點的值
3、獲得所有可選頂點,然后根據可選頂點填寫同級頂點的值
首先,我們需要提供一個類來創建鄰接矩陣。一個鄰接矩陣,首先需要傳入一個頂點數組:vertex,需要一個用來裝鄰接矩陣的數組:adjoinArray。剛剛我們上面說到了,這個類還必須提供計算并集和交集的方法:
export type AdjoinType = Array<string>;
export default class AdjoinMatrix {
vertex: AdjoinType; // 頂點數組
quantity: number; // 矩陣長度
adjoinArray: Array<number>; // 矩陣數組
constructor(vertx: AdjoinType) {
this.vertex = vertx;
this.quantity = this.vertex.length;
this.adjoinArray = [];
this.init();
}
// 初始化數組
init() {
this.adjoinArray = Array(this.quantity * this.quantity).fill(0);
}
/*
* @param id string
* @param sides Array<string>
* 傳入一個頂點,和當前頂點可達的頂點數組,將對應位置置為1
*/
setAdjoinVertexs(id: string, sides: AdjoinType) {
const pIndex = this.vertex.indexOf(id);
sides.forEach((item) => {
const index = this.vertex.indexOf(item);
this.adjoinArray[pIndex * this.quantity + index] = 1;
});
}
/*
* @param id string
* 傳入頂點的值,獲取該頂點的列
*/
getVertexCol(id: string) {
const index = this.vertex.indexOf(id);
const col: Array<number> = [];
this.vertex.forEach((item, pIndex) => {
col.push(this.adjoinArray[index + this.quantity * pIndex]);
});
return col;
}
/*
* @param params Array<string>
* 傳入一個頂點數組,求出該數組所有頂點的列的合
*/
getColSum(params: AdjoinType) {
const paramsVertex = params.map((id) => this.getVertexCol(id));
const paramsVertexSum: Array<number> = [];
this.vertex.forEach((item, index) => {
const rowtotal = paramsVertex
.map((value) => value[index])
.reduce((total, current) => {
total += current || 0;
return total;
}, 0);
paramsVertexSum.push(rowtotal);
});
return paramsVertexSum;
}
/*
* @param params Array<string>
* 傳入一個頂點數組,求出并集
*/
getCollection(params: AdjoinType) {
const paramsColSum = this.getColSum(params);
let collections: AdjoinType = [];
paramsColSum.forEach((item, index) => {
if (item && this.vertex[index]) collections.push(this.vertex[index]);
});
return collections;
}
/*
* @param params Array<string>
* 傳入一個頂點數組,求出交集
*/
getUnions(params: AdjoinType) {
const paramsColSum = this.getColSum(params);
let unions: AdjoinType = [];
paramsColSum.forEach((item, index) => {
if (item >= params.length && this.vertex[index]) unions.push(this.vertex[index]);
});
return unions;
}
}
有了這個類,接下來可以創建一個專門用于生成商品多規格選擇的類,它繼承于AdjoinMatrix。
我們這個多規格選擇的鄰接矩陣,需要提供一個查詢可選頂點的方法:getSpecscOptions
import AdjoinMatrix from "./adjoin-martix";
import { AdjoinType } from "./adjoin-martix";
import { SpecCategoryType, CommoditySpecsType } from "../redux/reducer/spec-reducer";
export default class SpecAdjoinMatrix extends AdjoinMatrix {
specList: Array<CommoditySpecsType>;
specCombinationList: Array<SpecCategoryType>;
constructor(specList: Array<CommoditySpecsType>, specCombinationList: Array<SpecCategoryType>) {
super(specList.reduce((total: AdjoinType, current) => [...total, ...current.list], []));
this.specList = specList;
this.specCombinationList = specCombinationList;
// 根據可選規格列表矩陣創建
this.initSpec();
// 同級頂點創建
this.initSameLevel();
}
/**
* 根據可選規格組合填寫鄰接矩陣的值
*/
initSpec() {
this.specCombinationList.forEach((item) => {
this.fillInSpec(item.specs);
});
}
// 填寫同級點
initSameLevel() {
// 獲得初始所有可選項
const specsOption = this.getCollection(this.vertex);
this.specList.forEach((item) => {
const params: AdjoinType = [];
// 獲取同級別頂點
item.list.forEach((value) => {
if (specsOption.includes(value)) params.push(value);
});
// 同級點位創建
this.fillInSpec(params);
});
}
/*
* 傳入頂點數組,查詢出可選規格
* @param params
*/
getSpecscOptions(params: AdjoinType) {
let specOptionCanchoose: AdjoinType = [];
if (params.some(Boolean)) {
// 過濾一下選項
specOptionCanchoose = this.getUnions(params.filter(Boolean));
} else {
// 所有可選項
specOptionCanchoose = this.getCollection(this.vertex);
}
return specOptionCanchoose;
}
/*
* @params
* 填寫鄰接矩陣的值
*/
fillInSpec(params: AdjoinType) {
params.forEach((param) => {
this.setAdjoinVertexs(param, params);
});
}
好了到了這一步,我們已經可以在頁面中使用這兩個類了:
import React, { useState, useMemo } from "react";
import { useSelector } from "react-redux";
import { RootState } from "../redux/reducer/root-reducer";
import SpecAdjoinMatrix from "../utils/spec-adjoin-martix";
import "./spec.css";
const classNames = require("classnames");
const Spec: React.FC = () => {
const { specList, specCombinationList } = useSelector((state: RootState) => state.spec);
// 已選擇的規格,長度為規格列表的長度
const [specsS, setSpecsS] = useState(Array(specList.length).fill(""));
// 創建一個規格矩陣
const specAdjoinMatrix = useMemo(() => new SpecAdjoinMatrix(specList, specCombinationList), [specList, specCombinationList]);
// 獲得可選項表
const optionSpecs = specAdjoinMatrix.getSpecscOptions(specsS);
const handleClick = function(bool: boolean, text: string, index: number) {
// 排除可選規格里面沒有的規格
if (specsS[index] !== text && !bool) return;
// 根據text判斷是否已經被選中了
specsS[index] = specsS[index] === text ? "" : text;
setSpecsS(specsS.slice());
};
return (
<div className="container">
{specList.map(({ title, list }, index) => (
<div key={index}>
<p className="title">{title}</p>
<div className="specBox">
{list.map((value, i) => {
const isOption = optionSpecs.includes(value); // 當前規格是否可選
const isActive = specsS.includes(value); // 當前規格是否被選
return (
<span
key={i}
className={classNames({
specOption: isOption,
specAction: isActive,
specDisabled: !isOption,
})}
onClick={() => handleClick(isOption, value, index)}
>
{value}
</span>
);
})}
</div>
</div>
))}
</div>
);
};
export default Spec;
好了,打完收工了,如果有小伙伴想看實現效果,可以查看這里,如果有小伙伴想把代碼拉到本地看看,那么請點擊這里
實踐證明:大學學的東西是真的有用的。我們通過圖,解決了商品多規格選擇的難題。在求解可選規格的時候,時間復雜度由原來的O(n2)變成了O(n)。不過值得一提的是,采用鄰接矩陣來存儲圖,空間復雜度就變成了O(n2)了,同時也存在浪費空間的問題,但是圖肯定不止有鄰接矩陣這一種存儲方法,我們還可以用鏈表來存儲圖,小伙伴們可以自己去試一試。另外如果用鏈表來存儲圖,空間復雜度會變低,但是時間復雜度會變高,具體如何選擇,就看小伙伴們自己權衡了。
以后遇到這個需求,小伙伴們肯定是分分鐘實現,提早下班。
我是覺非,碼字不易,如果你覺得這篇文章對你有用的話,請給個贊吧!!
作者:覺非
鏈接:https://juejin.im/post/5eef2fcee51d4574113a0203
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
、HTML標簽:表單標簽
1.1 表單概念:用于采集用戶輸入的數據的。用于和服務器進行交互。
1.2 語法:
form:用于定義表單的,可以定義一個范圍,范圍代表采集用戶數據的范圍
屬性:
action:指定提交數據的URL
method:指定提交方式
method分類:一共7種,2種比較常用
get:
1. 請求參數會在地址欄中顯示。會封裝到請求行中(HTTP協議后講解)。
2. 請求參數大小是有限制的。
3. 不太安全。
post:
1. 請求參數不會再地址欄中顯示。會封裝在請求體中(HTTP協議后講解)
2. 請求參數的大小沒有限制。
3. 較為安全。
注意:表單項中的數據要想被提交:必須指定其name屬性
二、表單項標簽:
2.1 input:可以通過type屬性值,改變元素展示的樣式
2.1.1 type屬性:
text:文本輸入框,默認值
placeholder:指定輸入框的提示信息,當輸入框的內容發生變化,會自動清空提示信息
password:密碼輸入框
radio:單選框
注意:
1. 要想讓多個單選框實現單選的效果,則多個單選框的name屬性值必須一樣。
2. 一般會給每一個單選框提供value屬性,指定其被選中后提交的值
3. checked屬性,可以指定默認值
checkbox:復選框
注意:
1. 一般會給每一個單選框提供value屬性,指定其被選中后提交的值
2. checked屬性,可以指定默認值
file:文件選擇框
hidden:隱藏域,用于提交一些信息。
按鈕:
submit:提交按鈕。可以提交表單
button:普通按鈕
image:圖片提交按鈕
src屬性指定圖片的路徑
label:指定輸入項的文字描述信息
注意:
label的for屬性一般會和 input 的id屬性值對應。如果對應了,則點擊label區域,會讓input輸入框獲取焦點。
select: 下拉列表,子元素option,指定列表項
textarea:文本域,cols指定列數,每一行有多少個字符,rows默認多少行。
三、CSS:頁面美化和布局控制
3.1 概念: Cascading Style Sheets 層疊樣式表
層疊:多個樣式可以作用在同一個html的元素上,同時生效
3.2 好處:
1. 功能強大
2. 將內容展示和樣式控制分離
3. 降低耦合度。解耦
4. 讓分工協作更容易
5. 提高開發效率
3.3 CSS的使用:CSS與html結合方式
3.3.1 內聯樣式:在標簽內使用style屬性指定css代碼
如:<div style="color:red;">hello css</div>
3.3.2 內部樣式:在head標簽內,定義style標簽,style標簽的標簽體內容就是css代碼
如:
<style>
div{
color:blue;
}
</style>
<div>hello css</div>
3.3.3 外部樣式
1. 定義css資源文件。
2. 在head標簽內,定義link標簽,引入外部的資源文件
如:
a.css文件:
div{
color:green;
}
html文件:
<link rel="stylesheet" href="css/a.css">
<div>hello css</div>
注意:
1,2,3種方式 css作用范圍越來越大
1方式不常用,后期常用2,3
3.4 css語法:
3.4.1 格式:
選擇器 {
屬性名1:屬性值1;
屬性名2:屬性值2;
...
}
* 選擇器:篩選具有相似特征的元素
* 注意:
* 每一對屬性需要使用;隔開,最后一對屬性可以不加;
3.5 css選擇器:篩選具有相似特征的元素
3.5.1 分類:
1. 基礎選擇器:
1. id選擇器:選擇具體的id屬性值的元素.建議在一個html頁面中id值唯一
* 語法:#id屬性值{}
2. 元素選擇器:選擇具有相同標簽名稱的元素
* 語法: 標簽名稱{}
* 注意:id選擇器優先級高于元素選擇器
3. 類選擇器:選擇具有相同的class屬性值的元素。
* 語法:.class屬性值{}
* 注意:類選擇器選擇器優先級高于元素選擇器
2. 擴展選擇器:
1. 選擇所有元素:
* 語法: *{}
2. 并集選擇器:
* 選擇器1,選擇器2{}
3. 子選擇器:篩選選擇器1元素下的選擇器2元素
* 語法: 選擇器1 選擇器2{}
4. 父選擇器:篩選選擇器2的父元素選擇器1
* 語法: 選擇器1 > 選擇器2{}
5. 屬性選擇器:選擇元素名稱,屬性名=屬性值的元素
* 語法: 元素名稱[屬性名="屬性值"]{}
6. 偽類選擇器:選擇一些元素具有的狀態
* 語法: 元素:狀態{}
* 如: <a>
* 狀態:
* link:初始化的狀態
* visited:被訪問過的狀態
* active:正在訪問狀態
* hover:鼠標懸浮狀態
3.6 css屬性:
1. 字體、文本
* font-size:字體大小
* color:文本顏色
* text-align:對其方式
* line-height:行高
2. 背景
* background:
3. 邊框
* border:設置邊框,符合屬性
4. 尺寸
* width:寬度
* height:高度
5. 盒子模型:控制布局
* margin:外邊距
* padding:內邊距
* 默認情況下內邊距會影響整個盒子的大小
* box-sizing: border-box; 設置盒子的屬性,讓width和height就是最終盒子的大小
* float:浮動
* left
* right
*請認真填寫需求信息,我們會在24小時內與您取得聯系。