CSS3 ordered list styles

本文介绍了一种使用CSS3来美化HTML有序列表的方法,通过利用计数器和伪元素实现自定义编号样式。

from:http://www.red-team-design.com/css3-ordered-list-styles

Styling ordered lists was always a tricky task and I’m not the only one who thinks that. To style numbers you need to remove default browser styles and add hooks to your lists elements in order to target them and style accordingly.

In this article you’ll learn how to add some CSS3 fine tuning to your ordered lists, using a semantic approach.

The idea

When I first read Roger Johansson’s article about styling ordered list numbers, I must admit I seriously felt in love with that technique. Using that technique, I will try to go a bit further and show you two different styling possibilities for ordered lists.

View demo

The HTML

Below you’ll find nothing than simple ordered list markup:

<ol class="rounded-list">
        <li><a href="">List item</a></li>
        <li><a href="">List item</a></li>
        <li><a href="">List item</a>
                <ol>
                        <li><a href="">List sub item</a></li>
                        <li><a href="">List sub item</a></li>
                        <li><a href="">List sub item</a></li>
                </ol>
        </li>
        <li><a href="">List item</a></li>
        <li><a href="">List item</a></li>
</ol>

The CSS

Further, I’ll try to explain how this works in a few words.

This technique uses Automatic counters and numbering. Basically it’s about using two CSS 2.1 properties: counter-reset (this initiate a counter) and counter-increment (kinda self-explanatory, this increments the previous counter). As you will see below, thecounter-increment will be used along with CSS generated content (pseudo-elements).

ol{
        counter-reset: li; /* Initiate a counter */
        list-style: none; /* Remove default numbering */
        *list-style: decimal; /* Keep using default numbering for IE6/7 */
        font: 15px 'trebuchet MS', 'lucida sans';
        padding: 0;
        margin-bottom: 4em;
        text-shadow: 0 1px 0 rgba(255,255,255,.5);
}

ol ol{
        margin: 0 0 0 2em; /* Add some left margin for inner lists */
}
Rounded-shaped numbers

.rounded-list a{
        position: relative;
        display: block;
        padding: .4em .4em .4em 2em;
        *padding: .4em;
        margin: .5em 0;
        background: #ddd;
        color: #444;
        text-decoration: none;
        border-radius: .3em;
        transition: all .3s ease-out;
}

.rounded-list a:hover{
        background: #eee;
}

.rounded-list a:hover:before{
    transform: rotate(360deg);
}

.rounded-list a:before{
        content: counter(li);
        counter-increment: li;
        position: absolute;
        left: -1.3em;
        top: 50%;
        margin-top: -1.3em;
        background: #87ceeb;
        height: 2em;
        width: 2em;
        line-height: 2em;
        border: .3em solid #fff;
        text-align: center;
        font-weight: bold;
        border-radius: 2em;
        transition: all .3s ease-out;
}
Rectangle-shaped numbers

.rectangle-list a{
        position: relative;
        display: block;
        padding: .4em .4em .4em .8em;
        *padding: .4em;
        margin: .5em 0 .5em 2.5em;
        background: #ddd;
        color: #444;
        text-decoration: none;
        transition: all .3s ease-out;
}

.rectangle-list a:hover{
        background: #eee;
}       

.rectangle-list a:before{
        content: counter(li);
        counter-increment: li;
        position: absolute;
        left: -2.5em;
        top: 50%;
        margin-top: -1em;
        background: #fa8072;
        height: 2em;
        width: 2em;
        line-height: 2em;
        text-align: center;
        font-weight: bold;
}

.rectangle-list a:after{
        position: absolute;
        content: '';
        border: .5em solid transparent;
        left: -1em;
        top: 50%;
        margin-top: -.5em;
        transition: all .3s ease-out;
}

.rectangle-list a:hover:after{
        left: -.5em;
        border-left-color: #fa8072;
}
Small bonus

Some CSS3 numbers animations are also included in this demo. Unfortunately, as far as I know and at this time, Firefox is the only one who supports animated pseudo-elements. Let’s hope that will improve sooner or later.

Browser support

These list looks well also on older browsers, as you can see below:

View demo

That’s all!

Thank you all for reading and I hope you enjoyed it. Feel free to share your thoughts, I appreciate your comments.


<template> <div class="editor" ref="editor" :style="styles"></div> </template> <script> import Quill from "quill"; import "quill/dist/quill.core.css"; import "quill/dist/quill.snow.css"; import "quill/dist/quill.bubble.css"; export default { name: "Editor", props: { /* 编辑器的内容 */ value: { type: String, default: "", }, /* 高度 */ height: { type: Number, default: null, }, /* 最小高度 */ minHeight: { type: Number, default: null, }, /* 只读 */ readOnly: { type: Boolean, default: false, } }, data() { return { Quill: null, currentValue: "", options: { theme: "snow", bounds: document.body, debug: "warn", modules: { // 工具栏配置 toolbar: [ ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线 ["blockquote", "code-block"], // 引用 代码块 [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表 [{ indent: "-1" }, { indent: "+1" }], // 缩进 [{ size: ["small", false, "large", "huge"] }], // 字体大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题 [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色 [{ align: [] }], // 对齐方式 ["clean"], // 清除文本格式 ["link", "image", "video"] // 链接、图片、视频 ], }, placeholder: "请输入内容", readOnly: this.readOnly, }, }; }, computed: { styles() { let style = {}; if (this.minHeight) { style.minHeight = `${this.minHeight}px`; } if (this.height) { style.height = `${this.height}px`; } return style; }, }, watch: { value: { handler(val) { if (val !== this.currentValue) { this.currentValue = val === null ? "" : val; if (this.Quill) { this.Quill.pasteHTML(this.currentValue); } } }, immediate: true, }, }, mounted() { this.init(); }, beforeDestroy() { this.Quill = null; }, methods: { init() { const editor = this.$refs.editor; this.Quill = new Quill(editor, this.options); this.Quill.pasteHTML(this.currentValue); this.Quill.on("text-change", (delta, oldDelta, source) => { const html = this.$refs.editor.children[0].innerHTML; const text = this.Quill.getText(); const quill = this.Quill; this.currentValue = html; this.$emit("input", html); this.$emit("on-change", { html, text, quill }); }); this.Quill.on("text-change", (delta, oldDelta, source) => { this.$emit("on-text-change", delta, oldDelta, source); }); this.Quill.on("selection-change", (range, oldRange, source) => { this.$emit("on-selection-change", range, oldRange, source); }); this.Quill.on("editor-change", (eventName, ...args) => { this.$emit("on-editor-change", eventName, ...args); }); }, }, }; </script> <style> .editor, .ql-toolbar { white-space: pre-wrap!important; line-height: normal !important; } .quill-img { display: none; } .ql-snow .ql-tooltip[data-mode="link"]::before { content: "请输入链接地址:"; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; content: "保存"; padding-right: 0px; } .ql-snow .ql-tooltip[data-mode="video"]::before { content: "请输入视频地址:"; } .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: "14px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { content: "10px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { content: "18px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { content: "32px"; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: "文本"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { content: "标题1"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { content: "标题2"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { content: "标题3"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { content: "标题4"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { content: "标题5"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { content: "标题6"; } .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: "标准字体"; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { content: "衬线字体"; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { content: "等宽字体"; } </style> 上传文件为vue文件,该文件vue版本为vue2版本,脚本为JavaScript编写;现在将该vue文件进行转化:转化vue版本为vue3版本,脚本为TypeScript编写
08-30
以下是转换后的Vue 3 + TypeScript版本: ```vue <template> <div class="editor" ref="editor" :style="styles"></div> </template> <script lang="ts"> import { defineComponent, ref, computed, watch, onMounted, onBeforeUnmount } from &#39;vue&#39;; import Quill from &#39;quill&#39;; import &#39;quill/dist/quill.core.css&#39;; import &#39;quill/dist/quill.snow.css&#39;; import &#39;quill/dist/quill.bubble.css&#39;; export default defineComponent({ name: &#39;Editor&#39;, props: { /* 编辑器的内容 */ value: { type: String, default: &#39;&#39;, }, /* 高度 */ height: { type: Number, default: null, }, /* 最小高度 */ minHeight: { type: Number, default: null, }, /* 只读 */ readOnly: { type: Boolean, default: false, } }, emits: [&#39;input&#39;, &#39;on-change&#39;, &#39;on-text-change&#39;, &#39;on-selection-change&#39;, &#39;on-editor-change&#39;], setup(props, { emit }) { const editor = ref<HTMLElement | null>(null); const quillInstance = ref<Quill | null>(null); const currentValue = ref(props.value); const options = { theme: &#39;snow&#39;, bounds: document.body, debug: &#39;warn&#39;, modules: { toolbar: [ [&#39;bold&#39;, &#39;italic&#39;, &#39;underline&#39;, &#39;strike&#39;], [&#39;blockquote&#39;, &#39;code-block&#39;], [{ list: &#39;ordered&#39; }, { list: &#39;bullet&#39; }], [{ indent: &#39;-1&#39; }, { indent: &#39;+1&#39; }], [{ size: [&#39;small&#39;, false, &#39;large&#39;, &#39;huge&#39;] }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], [{ align: [] }], [&#39;clean&#39;], [&#39;link&#39;, &#39;image&#39;, &#39;video&#39;] ], }, placeholder: &#39;请输入内容&#39;, readOnly: props.readOnly, }; const styles = computed(() => { const style: Record<string, string> = {}; if (props.minHeight) { style.minHeight = `${props.minHeight}px`; } if (props.height) { style.height = `${props.height}px`; } return style; }); watch(() => props.value, (val) => { if (val !== currentValue.value) { currentValue.value = val === null ? &#39;&#39; : val; if (quillInstance.value) { quillInstance.value.pasteHTML(currentValue.value); } } }, { immediate: true }); onMounted(() => { init(); }); onBeforeUnmount(() => { quillInstance.value = null; }); const init = () => { if (!editor.value) return; quillInstance.value = new Quill(editor.value, options); quillInstance.value.pasteHTML(currentValue.value); quillInstance.value.on(&#39;text-change&#39;, (delta, oldDelta, source) => { if (!editor.value) return; const html = editor.value.children[0].innerHTML; const text = quillInstance.value?.getText() || &#39;&#39;; currentValue.value = html; emit(&#39;input&#39;, html); emit(&#39;on-change&#39;, { html, text, quill: quillInstance.value }); }); quillInstance.value.on(&#39;text-change&#39;, (delta, oldDelta, source) => { emit(&#39;on-text-change&#39;, delta, oldDelta, source); }); quillInstance.value.on(&#39;selection-change&#39;, (range, oldRange, source) => { emit(&#39;on-selection-change&#39;, range, oldRange, source); }); quillInstance.value.on(&#39;editor-change&#39;, (eventName, ...args) => { emit(&#39;on-editor-change&#39;, eventName, ...args); }); }; return { editor, styles, }; }, }); </script> <style> .editor, .ql-toolbar { white-space: pre-wrap!important; line-height: normal !important; } .quill-img { display: none; } .ql-snow .ql-tooltip[data-mode="link"]::before { content: "请输入链接地址:"; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; content: "保存"; padding-right: 0px; } .ql-snow .ql-tooltip[data-mode="video"]::before { content: "请输入视频地址:"; } .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: "14px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { content: "10px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { content: "18px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { content: "32px"; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: "文本"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { content: "标题1"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { content: "标题2"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { content: "标题3"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { content: "标题4"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { content: "标题5"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { content: "标题6"; } .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: "标准字体"; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { content: "衬线字体"; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { content: "等宽字体"; } </style> ``` 主要变更点: 1. 使用`defineComponent`定义组件 2. 使用Composition API替代Options API 3. 使用`ref`和`computed`等Vue 3响应式API 4. 使用TypeScript类型标注 5. 生命周期钩子改为`onMounted`和`onBeforeUnmount` 6. 明确声明了`emits`事件 7. 使用`setup`函数组织逻辑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值