java 参仿京东天猫的分类树结构,实现无限级分类结构


参考于京东和天猫的分类树结构,整理了个人在做电商项目分类树的开发思路及演变过程:

第一种方式:

纯粹的采用Java代码和SQL语句方式,不断的递归查询,其自身是否拥有子节点,这样有不好的缺点就是,访问时数据SQL链接比较多,占用了数据库链接。

实现思路:

  • 查询是否拥有子节点的SQL是:

select c.* from category c  where  c.status = 1 and c.parent_id=?

  • 实现组装的TreeNo:

// 分类ID

private Long categoryId;

// 分类名称

private String categoryName;

private List<
TreeNo
> childrens;

不断通过 
 java 递归方式去获取该节点是否拥有子节点,如果拥有则继续往下递归,直到递归到没有子节点。

方式:比较影响数据库的访问,可以通过MemcachedCache缓存起来,定时的刷新分类树缓存。

第二种方式:

利用
函数
Function遍历一张
数据库数据
表中分类树关系:

如:

CREATE DEFINER=`localhot` FUNCTION `Tree`(treeId INT) RETURNS VARCHAR(4000) CHARSET utf8
BEGIN
DECLARE sTemp VARCHAR(4000);
DECLARE sTempChd VARCHAR(4000);
SET sTemp = ‘$‘;
SET sTempChd = CAST(treeId AS CHAR);
WHILE sTempChd IS NOT NULL DO
SET sTemp = CONCAT(sTemp,‘,‘,sTempChd);
SELECT GROUP_CONCAT(id) INTO sTempChd FROM Category WHERE FIND_IN_SET(parent_id,sTempChd)>0;
END WHILE;
RETURN sTemp;
END$$
DELIMITER

返回当前节点的晚辈集合:

再去分析出来,进行数据组装;

第三种方式:

取出表中的全部SQL数据,再去组装分类树的结构数据:

参考 Java 多叉树的实现,完成树的初始化和遍历

public final class CategoryHelper {

	private static  TreeNode root;

	public  static  HashSet<TreeNode> tempNodeList;

	private static CategoryHelper treeHelper;

	private final static Lock lock =new ReentrantLock();   //创建一个锁

	private CategoryHelper() {

		/**
		 * TODO 加载分类树
		*/

	}

	public static CategoryHelper getInstance() {
		if (treeHelper == null) {
			lock .lock();
			try {
				if (treeHelper == null) {
					treeHelper = new CategoryHelper();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
		return treeHelper;
	}

	/**
	 *  找到一颗树中某个节点
	 * @param tree
	 * @param id
	 * @return
	 */
	public static TreeNode getTreeNodeById(TreeNode tree, int id) {
		if (tree == null)
			return null;
		TreeNode treeNode = tree.findTreeNodeById(id);
		return treeNode;
	}

	/**
	 * 根据节点ID找到TreeNote
	 */
	public static TreeNode getTreeNodeById(int id){
		if(id==-1)
			return null;
		HashMap<String,TreeNode> hashMap=getNodesIntoMap();
		TreeNode node=hashMap.get(String.valueOf(id));
		return node;
	}

	 /** generate a tree from the given treeNode or entity list
      *  生成树或实体由给定的treeNode名单
     *
     * */
	public static void generateTree() {
		HashMap nodeMap = getNodesIntoMap();
		Iterator it = nodeMap.values().iterator();
		while (it.hasNext()) {
			TreeNode treeNode = (TreeNode) it.next();
			int parentId = treeNode.getParentId();
			String parentKeyId = String.valueOf(parentId);
			if (nodeMap.containsKey(parentKeyId)) {
				TreeNode parentNode = (TreeNode) nodeMap.get(parentKeyId);
				if (parentNode == null) {
					return;
				} else {
					parentNode.addChildNode(treeNode);
				}
			}
		}

	}

	 /**
     * set the parent nodes point to the child nodes
     * 设置子节点集合添加到父节点上。
     * @param nodeMap
     *            a hashmap that contains all the treenodes by its id as the key
     *            一个hashmap,包括所有treenodes由其id作为关键
     */
	@Deprecated
	private  void putChildIntoParent(HashMap nodeMap) {
		Iterator it = nodeMap.values().iterator();
		while (it.hasNext()) {
			TreeNode treeNode = (TreeNode) it.next();
			int parentId = treeNode.getParentId();
			String parentKeyId = String.valueOf(parentId);
			if (nodeMap.containsKey(parentKeyId)) {
				TreeNode parentNode = (TreeNode) nodeMap.get(parentKeyId);
				if (parentNode == null) {
					return;
				} else {
					parentNode.addChildNode(treeNode);
				}
			}
		}
	}

	/**
     * put all the treeNodes into a hash table by its id as the key
     *  把所有的treeNodes成一张哈希表由其id作为关键
     * @return hashmap that contains the treenodes
     */
	protected static HashMap<String,TreeNode> getNodesIntoMap() {
		int maxId = Integer.MAX_VALUE;
		HashMap<String,TreeNode> nodeMap = new HashMap<String, TreeNode>();
		Iterator<TreeNode> it = tempNodeList.iterator();
		while (it.hasNext()) {
			TreeNode treeNode = (TreeNode) it.next();
			int id = treeNode.getSelfId();
			if (id < maxId) {
				maxId = id;
				root = treeNode;
			}
			String keyId = String.valueOf(id);
			nodeMap.put(keyId, treeNode);
		}
		return nodeMap;
	}

	/**
	 * add a tree node to the tempNodeList
     *  添加一个树节点的tempNodeList
     *
     * */
	public static void addTreeNode(TreeNode treeNode) {
		lock.lock();
		try {
			 boolean insertFlag =root.insertJuniorNode(treeNode); //将已经产生的树节点插入到树
			   if (insertFlag) {
				   tempNodeList.add(treeNode);
			   }
			   else{
				   throw new BusinessException(treeNode.getParentId()+"这父节点不存在", ErrorCode.BUSINESS_ERROR);
			   }
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}

	/* 删除节点和它下面的晚辈 */
	public static void delTreeNode(TreeNode treeNode){
		boolean absent = tempNodeList.contains(treeNode);
		if(absent){
			lock.lock();
			try {
				List<TreeNode> delTempNodeList = new ArrayList<TreeNode>();
				List<TreeNode> childList = treeNode.getJuniors();
				if (AppUtils.isNotBlank(childList)) {
					for (int i = 0; i < childList.size(); i++) {
						delTempNodeList.add(childList.get(i));
					}
				}
				delTempNodeList.add(treeNode);
				tempNodeList.removeAll(delTempNodeList); // 更新tempNodeList
				treeNode.deleteNode();
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
		}
	}

	/* 删除当前节点的某个子节点 */
	public static void delTreeNode(TreeNode treeNode,int childId){
		boolean absent = tempNodeList.contains(treeNode);
		if(absent){
			lock.lock();
			try {
				List<TreeNode> childList = treeNode.getChildList();
				if(AppUtils.isNotBlank(childList)){
					int childNumber = childList.size();
					for (int i = 0; i < childNumber; i++) {
						TreeNode child = childList.get(i);
						if (child.getSelfId() == childId) {
							tempNodeList.remove(child);
							treeNode.deleteChildNode(childId);
						}
					}

				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
		}
	}

	/**
	 * 更新树节点
	 * @param sourceTreeNode 源对象
	 * @param targetTreeNode 目标对象
	 */
	public static void updateTreeNode(TreeNode sourceTreeNode,String noteName){
		boolean absent = tempNodeList.contains(sourceTreeNode);
		if(absent){

			lock.lock();
			try {
				sourceTreeNode.setNodeName(noteName);
				Object obj=sourceTreeNode.getObj();
				if(obj!=null && obj instanceof OrganizationEntity){
					OrganizationEntity entity=(OrganizationEntity)obj;
					entity.setOrgName(noteName);
				}
			    tempNodeList.add(sourceTreeNode);
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
		}
	}

	/**
	 * 更新树节点
	 * @param sourceTreeNode 源对象
	 * @param targetTreeNode 目标对象
	 */
	public static void updateTreeNode(TreeNode sourceTreeNode,TreeNode targetTreeNode){
		boolean absent = tempNodeList.contains(sourceTreeNode);
		if(absent){
			lock.lock();
			try {
				sourceTreeNode.setNodeName(targetTreeNode.getNodeName());
				OrganizationEntity entity=new OrganizationEntity(targetTreeNode.getParentId(), targetTreeNode.getSelfId(), targetTreeNode.getNodeName());
				sourceTreeNode.setObj(entity);
				tempNodeList.add(sourceTreeNode);
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
		}
	}

	  /**
     * adapt the entities to the corresponding treeNode
     *  适应于相应的treeNode的实体
     * @param entityList
     *            list that contains the entities       列表包含的实体
     *@return the list containg the corresponding treeNodes of the entities
     *  containg相应的treeNodes名单的实体
     */
	public static HashSet<TreeNode> changeEnititiesToTreeNodes(Set entityList) {
		OrganizationEntity orgEntity = new OrganizationEntity();
		Set<TreeNode> tempNodeList = new HashSet<TreeNode>();
		Map<Integer, TreeNode> map=new HashMap<Integer, TreeNode>();
		TreeNode treeNode;

		Iterator<OrganizationEntity> it = entityList.iterator();
		while (it.hasNext()) {
			orgEntity = (OrganizationEntity) it.next();
			treeNode = new TreeNode();
			treeNode.setObj(orgEntity);
			treeNode.setParentId(orgEntity.getParentId());
			treeNode.setSelfId(orgEntity.getOrgId());
			treeNode.setNodeName(orgEntity.getOrgName());
			treeNode.setParentNode(map.get(orgEntity.getParentId()));
			tempNodeList.add(treeNode);
			map.put(orgEntity.getOrgId(), treeNode);
		}
		return (HashSet<TreeNode>) tempNodeList;
	}

	public static TreeNode getRoot() {
		return root;
	}

	public static HashSet<TreeNode> getTempNodeList() {
		return tempNodeList;
	}

	public  static void setTempNodeList(HashSet<TreeNode> tempNodeList) {
		CategoryHelper.tempNodeList = tempNodeList;
	}

}
public class TreeNode implements Serializable {

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	private int parentId;

	private int selfId;

	protected String nodeName;

	protected Object obj;

	protected TreeNode parentNode;

	protected List<TreeNode> childList;

	public TreeNode() {
		initChildList();
	}

	public TreeNode(TreeNode parentNode) {
		this.getParentNode();
		initChildList();
	}

	public boolean isLeaf() {
		if (childList == null) {
			return true;
		} else {
			if (childList.isEmpty()) {
				return true;
			} else {
				return false;
			}
		}
	}

	/* 插入一个child节点到当前节点中 */
	public void addChildNode(TreeNode treeNode) {
		initChildList();
		childList.add(treeNode);
	}

	public void initChildList() {
		if (childList == null)
			childList = new ArrayList<TreeNode>();
	}

	public boolean isValidTree() {
		return true;
	}

	/* 返回当前节点的父辈节点集合 */
	public List<TreeNode> getElders() {
		List<TreeNode> elderList = new ArrayList<TreeNode>();
		TreeNode parentNode = this.getParentNode();
		if (parentNode == null) {
			return elderList;
		} else if(parentNode.getParentId()==-1) {
			return elderList;
		}else{
			elderList.add(parentNode);
			elderList.addAll(parentNode.getElders());
			return elderList;
		}
	}

	/* 返回当前节点的晚辈集合 */
	public List<TreeNode> getJuniors() {
		List<TreeNode> juniorList = new ArrayList<TreeNode>();
		List<TreeNode> childList = this.getChildList();
		if (childList == null) {
			return juniorList;
		} else {
			int childNumber = childList.size();
			for (int i = 0; i < childNumber; i++) {
				TreeNode junior = childList.get(i);
				juniorList.add(junior);
				juniorList.addAll(junior.getJuniors());
			}
			return juniorList;
		}
	}

	/* 返回当前节点的同级的集合 */
	public List<TreeNode> getCompatriots(){
		List<TreeNode> compatriotList = new ArrayList<TreeNode>();
		TreeNode parentNode = this.getParentNode();
		if (parentNode == null) {
			return compatriotList;

		}else if(this.getParentId()==0){ //说明是一级节点
			compatriotList=getParentNode().getChildList();
		} else if(parentNode.getParentId()==-1) { //说明是根节点
			compatriotList= this.getChildList();
		}else{
			compatriotList.add(this.getParentNode());
			compatriotList.addAll(parentNode.getChildList());
		}
		return compatriotList;
	}

	/* 返回当前节点的孩子集合 */
	public List<TreeNode> getChildList() {
		return childList;
	}

	/* 删除节点和它下面的晚辈 */
	public void deleteNode() {
		TreeNode parentNode = this.getParentNode();
		int id = this.getSelfId();

		if (parentNode != null) {
			parentNode.deleteChildNode(id);
		}
	}

	/* 删除当前节点的某个子节点 */
	public void deleteChildNode(int childId) {
		List<TreeNode> childList = this.getChildList();
		int childNumber = childList.size();
		for (int i = 0; i < childNumber; i++) {
			TreeNode child = childList.get(i);
			if (child.getSelfId() == childId) {
				childList.remove(i);
				return;
			}
		}
	}

	/* 动态的插入一个新的节点到当前树中 */
	public boolean insertJuniorNode(TreeNode treeNode) {
		int juniorParentId = treeNode.getParentId();
		if(juniorParentId==0){
			addChildNode(treeNode);
			return true;
		}
		else if (this.parentId == juniorParentId) {
			List<TreeNode> list=this.getParentNode().getChildList();
			if(list==null || list.isEmpty()){
				list = new ArrayList<TreeNode>();
				list.add(treeNode);
			}else{
				list.add(treeNode);
			}
			return true;
		} else {
			List<TreeNode> childList = this.getChildList();
			int childNumber = childList.size();
			boolean insertFlag;

			for (int i = 0; i < childNumber; i++) {
				TreeNode childNode = childList.get(i);
				insertFlag = childNode.insertJuniorNode(treeNode);
				if (insertFlag == true)
					return true;
			}
			return false;
		}
	}

	/* 找到一颗树中某个节点 */
	public TreeNode findTreeNodeById(int id) {
		if (this.selfId == id)
			return this;
		if (childList.isEmpty() || childList == null) {
			return null;
		} else {
			int childNumber = childList.size();
			for (int i = 0; i < childNumber; i++) {
				TreeNode child = childList.get(i);
				TreeNode resultNode = child.findTreeNodeById(id);
				if (resultNode != null) {
					return resultNode;
				}
			}
			return null;
		}
	}

	/* 遍历一棵树,层次遍历 */
	public void traverse() {
		if (selfId < 0)
			return;
		print(this.selfId);
		if (childList == null || childList.isEmpty())
			return;
		int childNumber = childList.size();
		for (int i = 0; i < childNumber; i++) {
			TreeNode child = childList.get(i);
			child.traverse();
		}
	}

	public void print(String content) {
		System.out.println(content);
	}

	public void print(int content) {
		System.out.println(String.valueOf(content));
	}

	public void setChildList(List<TreeNode> childList) {
		this.childList = childList;
	}

	public int getParentId() {
		return parentId;
	}

	public void setParentId(int parentId) {
		this.parentId = parentId;
	}

	public int getSelfId() {
		return selfId;
	}

	public void setSelfId(int selfId) {
		this.selfId = selfId;
	}

	public TreeNode getParentNode() {
		return parentNode;
	}

	public void setParentNode(TreeNode parentNode) {
		this.parentNode = parentNode;
	}

	public String getNodeName() {
		return nodeName;
	}

	public void setNodeName(String nodeName) {
		this.nodeName = nodeName;
	}

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + selfId;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof TreeNode){
			TreeNode node=(TreeNode)obj;
			return this.selfId==node.getSelfId();
		}
		return false;
	}

	public TreeNode(int parentId, int selfId, String nodeName, Object obj) {
		super();
		this.parentId = parentId;
		this.selfId = selfId;
		this.nodeName = nodeName;
		this.obj = obj;
	}

}
public class OrganizationEntity {

	public int parentId;

	public int orgId;

	public String orgName;

	public int getParentId() {
		return parentId;
	}

	public void setParentId(int parentId) {
		this.parentId = parentId;
	}

	public int getOrgId() {
		return orgId;
	}

	public void setOrgId(int orgId) {
		this.orgId = orgId;
	}

	public String getOrgName() {
		return orgName;
	}

	public void setOrgName(String orgName) {
		this.orgName = orgName;
	}

	public OrganizationEntity(){

	}

	public OrganizationEntity(int parentId, int orgId, String orgName) {
		super();
		this.parentId = parentId;
		this.orgId = orgId;
		this.orgName = orgName;
	}

}

数据库表结构设计:

方式一:通过一张表来实现分类树:

CREATE TABLE `ls_category` (
  `id` bigint(20) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品类目ID‘,
  `parent_id` bigint(20) DEFAULT NULL COMMENT ‘父节点‘,
  `name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品类目名称‘,
  `pic` varchar(300) DEFAULT NULL COMMENT ‘类目的显示图片‘,
  `seq` int(5) DEFAULT NULL COMMENT ‘排序‘,
  `enable` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘
  `keyword` varchar(256) DEFAULT NULL COMMENT ‘SEO关键字‘,
  `cat_desc` varchar(256) DEFAULT NULL COMMENT ‘SEO描述‘,
  `title` varchar(256) DEFAULT NULL COMMENT ‘SEO标题‘,
  `rec_date` datetime DEFAULT NULL COMMENT ‘记录时间‘,
  `level` int(2) DEFAULT NULL COMMENT ‘分类层级‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘产品类目‘;

方式二:用两种表结构实现产品的分类树,表一:存放商品分类的一级分类 表二:存放商品分类的无限下级分类,及关联一级分类表的ID引用:

CREATE TABLE `ls_first_cat` (
  `first_cat_id` int(11) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品分类ID‘,
  `first_cat_name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品分类名称‘,
  `first_cat_picture` varchar(250) DEFAULT NULL COMMENT ‘分类的显示图片‘,
  `seq` int(11) DEFAULT NULL COMMENT ‘排序‘,
  `status` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘
  PRIMARY KEY (`first_cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘商品分类‘;

CREATE TABLE `ls_cat` (
  `cat_id` int(11) NOT NULL DEFAULT ‘0‘ COMMENT ‘产品分类ID‘,
  `cat_name` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘产品分类名称‘,
  `first_cat_id` varchar(250) DEFAULT NULL COMMENT ‘对应的一级类型‘,
  `seq` int(11) DEFAULT NULL COMMENT ‘排序‘,
  `status` int(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘默认是1,表示正常状态,0为下线状态‘,
  `parent_cat_id` int(11)  COMMENT ‘父节点ID‘
  PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘商品子类表‘;

资料参考:

http://my.oschina.net/bootstrap/blog/166805

时间: 2024-11-02 14:18:43

java 参仿京东天猫的分类树结构,实现无限级分类结构的相关文章

【源码分享下载】每日更新之高仿京东商城

高仿京东商城 服务分类: 其他 使用服务: 其他 功能分类: 生活 支持平台: Android 运行环境: Android 开发语言: Java 开发工具: Eclipse 源码大小: 5.51MB 下载地址:http://www.devstore.cn/code/info/87.html 源码简介 仿照京东商城做出的APP(仅实现了部分界面) 源码片段 ? 源码运行截图:

iOS仿京东分类菜单之UICollectionView内容

 iOS仿京东分类菜单之UICollectionView内容 在 上<iOS仿京东分类菜单实例实现>已经实现了大部分主体的功能,本文是针对右边集合列表进行修改扩展,使它达到分组的效果,本文涉及到的主要是UICollectionView的知识内容,左边列表的实现见上一篇文章,先看实现的效果图: 一:实体的创建 1.1分组实体的创建(tagID跟左边表格进行关联,roomArray是存放房间的数组,也就是单元格的集合) #import <Foundation/Foundation.h>

Java爬虫爬取 天猫 淘宝 京东 搜索页和 商品详情

Java爬虫爬取 天猫 淘宝 京东 搜索页和 商品详情 先识别商品url,区分平台提取商品编号,再根据平台带着商品编号爬取数据. 1.导包 <!-- 爬虫相关Jar包依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.10-FINAL</version> </

浅谈android中仅仅使用一个TextView实现高仿京东,淘宝各种倒计时

今天给大家带来的是仅仅使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.最近公司一直加班也没来得及时间去整理,今天难得休息想把这个分享给大家,只求共同学习,以及自己后续的复习.为什么会想到使用一个TextView来实现呢?因为最近公司在做一些优化的工作,其中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的,实现的代码冗余比较大,故此项目经理就说:小宏这个就交给你来优化了,并且还要保证有一定的扩展性,当时就懵逼了.不知道从何处开始

仿京东商城左侧的一个导航条特效

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Typ

浅谈android中只使用一个TextView实现高仿京东,淘宝各种倒计时

今天给大家带来的是只使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.近期公司一直加班也没来得及时间去整理,今天难得歇息想把这个分享给大家.只求共同学习,以及自己兴许的复习. 为什么会想到使用一个TextView来实现呢?由于近期公司在做一些优化的工作,当中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的.实现的代码冗余比較大.故此项目经理就说:小宏这个就交给你来优化了.而且还要保证有一定的扩展性,当时就懵逼了.不知道从何处開始

竖向导航-仿京东左侧导航大类效果

完整代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Con

Android GridView 分组显示(仿京东商品分类)

Android GridView 分组显示(仿京东商品分类) Gridview分组显示, 仿京东分类效果 GridView 分组显示使用的第三方开源库: StickyGridHeadersGridView , 下载地址: https://github.com/TonicArtos/StickyGridHeaders 大致实现步骤: 1. adapter:  SPCategoryRightAdapter extends BaseAdapter implements StickyGridHeader

仿京东首页商品分类底部色标随鼠标移动特效

原文:仿京东首页商品分类底部色标随鼠标移动特效 今天扒皮下京东商品展示区的一个特效: 大家可以自行去京东看下特效,下面是这个特效的动态图(这次聪明的我懂得给图加水印了) 理下思路,每个分类选项卡宽度一致,且有一条灰色底边,然后默认有一条红色的色标让它漂浮在首个选项卡上面(其中的小三角形可以自己做个图),默认首个选项卡的文本(像上图是"AAA")为红色,其它选项卡的为灰色.默认除第一个选项卡对应的下方的内容DIV显示外,其它选项卡对应的内容DIV隐藏: 鼠标移到某个选项卡则改变该选项卡字