Jex’s Note

前端 HTML/CSS/JS

SEO

搜尋引擎將在子網域的網站視為完全不同的站

而將子目錄視為一個已存在的網站組成的一部份

加上 rich snippet

這是一個提供額外的資訊當在搜尋引擎列出結果時, 然候你要列出來的資訊有很多型態, 可能是你的產品, 額外想要顯示的售價等等

型態被定義在 schema.org, 你必須先確定你想顯示的資料型態為何, 再按照這個型態所定義的標籤屬性

例如 Product, 格式就要參考 schema.org/Product, code 請參考 Google 範例

如何加上? 在 body 或 header 任意地方加上 schema 規定的屬性 即可, 可以利用 google - Structured Data Testing Tool 幫忙檢查你要加上的是否被正確分析 :

<div itemscope itemtype="http://schema.org/Book">
    <span itemprop="name"> Inbound Marketing and SEO: Insights from the Moz Blog</span>
    <span itemprop="author">Rand Fishkin</span>
</div>

可參考 此站

覆寫網站所有字型

html * {
    font-family: 'Arial Black', 'Arial', "新細明體", 'PMingLiU', 'sans-serif';
}
  • PMingLiU : 新細明體的英文版名稱
  • sans-serif : 無襯線字

base64 圖檔

<img src="data:image/jpeg;base64,/9j/4AAQSknK2h//2Q==" alt="" />

Preview image and get actual width and height

HTML :

<input type="file" id="add-photo" name="add_photo">
<img width="200" height="100" id="add-photo_preview"/>

JS :

$('#add-photo').on('change', function (e) {
    // Create img
    var tempImg = document.createElement('img');
    console.log(document.querySelector('#add-photo').files[0]);  // File object
    // Put local image object into tempImg
    tempImg.src = window.URL.createObjectURL(document.querySelector('#add-photo').files[0]);
    tempImg.onload = function() {
        // Render image
        window.URL.revokeObjectURL(this.src);
        // We can get actual width and height.
        console.log(this.width);
        console.log(this.height);
    };
    $("#add-photo_preview").attr('src', tempImg.src);
});

Get checkbox array

HTML :

<input type="checkbox" class="courses" data-course-id="1" value="1">
<input type="checkbox" class="courses" data-course-id="2" value="2">

JS :

var courses = [];
$(".courses:checked").each(function() {
    courses.push($(this).data('course-id'));
});

Get radio value

HTML :

<input type="radio" name="role" id="role-student" value="student" checked>
<input type="radio" name="role" id="role-parent" value="parent">

JS :

role = $('input[name=role]:checked').val();

name 是陣列的話要加上引號
$('input[name="user[domain]"]:checked').length  // 沒選擇為 0, 選擇其中一項為 1

判斷 group radio 其中一項有沒有被選取

$('input:radio[name=language_preference]').is(":checked")

只返回 true 或 false,

uncheck radio/checkbox group

radio :

$('input[name=sex]').each(function () {
    $(this).prop('checked', false);
});

checkbox :

$("input[name='exercises[]']").each(function () {
    $(this).prop('checked', false);
});

Select 初始完後直接觸發

$('#exercises').on('change', funciton () {
    // do something
}).trigger('change');

Javascript tirgger HTML5 native validation

HTML:

<form id="test_form">
    <input type="text" name="location" required/>
</form>

JS:

$('#test_form').on('click', function () {
    if (test_form.checkValidity()) {
        console.log('ok');
    } else {
        console.log('fail');
    }
});

捲軸移動超過 content, sidebard 跟著移動

HTML:

<div id="header">Header</div>
<div id="wrapper">
    <div id="left">
        <div id="sidebar">Sidebar Text here!</div>
    </div>
    <div id="right">This is the text of the main part of the page.</div>
    <div class="clear"></div>
</div>
<div id="footer">Footer</div>

CSS:

#header {
    background: #c2c2c2;
    height: 50px;
}
#wrapper {
    position: relative;
    min-height: 500px; /* Just as an example */
    width: 500px;
}
#left {
    position: absolute;
    background: #d7d7d7;
    width: 150px;
    height: 100%;
}
#right {
    position: relative;
    width: 350px;
    float: right;
}
#sidebar {
    background: #0096d7;
    width: 150px;
    color: #fff;
}
.clear {
    clear: both;
}
#footer {
    background: #c2c2c2;
    height: 500px; /* Just as an example */
}

JS:

$(document).ready(function () {
    var length = $('#left').height() - $('#sidebar').height() + $('#left').offset().top;
    $(window).scroll(function () {
        var scroll = $(this).scrollTop();
        var height = $('#sidebar').height() + 'px';
        if (scroll < $('#left').offset().top) {
            $('#sidebar').css({
                'position': 'absolute',
                'top': '0'
            });
        } else if (scroll > length) {
            $('#sidebar').css({
                'position': 'absolute',
                'bottom': '0',
                'top': 'auto'
            });
        } else {
            $('#sidebar').css({
                'position': 'fixed',
                'top': '0',
                'height': height
            });
        }
    });
});

或直接用套件 Sticky-Kit, 用法很簡單

記得在 wrapper 底下做 clear: both, 否則 sidebar 太長會穿過 wrapper

ref: FIXING A SIDEBAR WHILE SCROLLING, UNTIL BOTTOM WITH JQUERY

HTML5 Sortable

HTML:

<script type="text/javascript" src="js/jquery.sortable.min.js"></script>
未分班學生 :
<ul class="name_container">
</ul>
己分班學生
<ul class="name_container">
</ul>

JS:

// 學生名字標簽可拖曳
$('.name_container').sortable({
    connectWith: '.name_container'
});

CS:

/* 所有名稱標簽的長寬 */
.name_container {
    min-height: 100px;
    width: 200px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    list-style-type: none;
    padding: 0px;
    margin: 0px;
}
.name_container li {
    border: 1px solid #CCC;
    background: #F6F6F6;
    font-family: "Tahoma";
    color: #1C94C4;
    margin: 2px;
    height: 30px;
    line-height: 30px;
    overflow: hidden;
    white-space: nowrap;
    text-align: center;
}
.name_container li.highlight {
    background: #FEE25F;
}

/* 拖移時顯示的預放位置以虛線顯示 */
li.sortable-placeholder {
    border: 1px dashed #CCC;
    background: none;
}

overflow-x: scroll 水平排列

HTML:

<div class="container">
    <div class="item">item1</div>
    <div class="item">item2</div>
    <div class="item">item3</div>
    <div class="item">item4</div>
</div>

CSS:

.container{
    white-space: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
}
.item {
    vertical-align: top;
    width: 300px;
    display: inline-block;
}

audio speed control

JS :

function initAudio(){
    var audio = new Audio();
    audio.src = "http://downloads.bbc.co.uk/learningenglish/features/6min/151105_6min_english_plastic_bags_download.mp3";
    audio.play();
    var speedlist = document.getElementById("speedlist");
    speedlist.addEventListener("change",changeSpeed);
    function changeSpeed(event){
        audio.playbackRate = event.target.value;
    }
}
window.addEventListener("load", initAudio);

HTML :

<select id="speedlist">
  <option value="1">change speed</option>
  <option value=".5">.5</option>
  <option value="1">Normal</option>
  <option value="1.5">1.5</option>
  <option value="2">2</option>
</select>

判斷滑鼠 mouse event which

$('#mylink').click(function(e){
    if ( (e.which == 1) ) {
        alert("left button");
    } else if ( (e.which == 2) ) {
        alert("middle button");
    } else if ( (e.which == 3) ) {
        alert("right button");
    }
});

失效則使用 e.preventDefault()

js l10n

util.js :

(function () {
    var L10N = window.L10N || {};
    window.L10N = L10N;

    if (!L10N.util) {
        L10N.util = {};

        L10N.util.getTrans = function (key, token) {
            if (typeof L10N.lang[key] === 'undefined') {
                return 'undefine-string!!';
            }
            var value = L10N.lang[key];
            if (typeof token === 'object') {
                for (var i in token) {
                    value = value.replace('{' + i + '}', token[i]);
                }
            }
            return value;
        };

        L10N.util.htmlspecialchars = function (str) {
            return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
        };
        L10N.util.htmlspecialchars_decode = function (str) {
            return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"");
        };
        L10N.util.nl2br = function (str) {
            return str.replace(/\n/g, "<br>");
        };
        L10N.util.trimDot = function (value) {
            return value.replace(/[.\s]+$/, '');
        };
    }

})();

lang/zh-TW/l10n.js :

(function () {
    var L10N = window.L10N || {};
    L10N.lang = L10N.lang || {};
    // common
    L10N.lang['admin-common-system_info'] = '系统讯息';

    // system - group
    L10N.lang['admin-system-group-edit_success'] = '修改成功';
})();

test/main.js :

alert(L10N.util.getTrans('admin-common-system_info'));

一些觀念

  • AngularsJS 與 bootstrap 一起使用的話要把 angular 的 $ 換掉
  • Angular 建議用第 2 版, 聽說改善很多效能問題
  • iOS 可用 react native, android 的 react native 要再等等
  • ios react native = javscript + swift
  • react native 是 native app 不是 web app

jQuery validate

安裝

下載頁面, download 載下來是一大包, 只需要 dist/jquery.validate.min.js 就好

或 Rails gem

gem 'jquery-validation-rails', '~> 1.13.1'
bundle install

assets/javascripts/application.js :

//= require jquery.validate
//= require jquery.validate.localization/messages_zh_TW
//# require jquery.validate.additional-methods

Rails 記得重啟才會生效

Example

$("#my-form").validate({

  rules: {
    'user[first]': {
      required: true
    }
  },

  // Specify the validation error messages
  messages: {
    'user[first]' : {
      required: 'First 未填'
    }
  },

  submitHandler: function(form) {
    form.submit();
  }
});

<button type="submit" class="btn btn-success">submit</button>

送出後 validate 會自動攔截 submit 行為, 通過後才會送出

validate

required : true
equalTo : "#confirmed_password"
rangelength : [2, 20]
minlength: 8

custom validation :

$.validator.addMethod(
  "alphabets", function(value, element, regexpr) {
    return regexpr.test(value);
  },"請輸入英文字母");

alphabets : /^[A-Za-z]{2,20}$/

預設是無法 Validate Hidden field 的, 加上 ignore: "" 就可以了

$("#form1").validate({
    ignore: "",
    rules: {
        something: {
            number:true,
            min:1,
            required:true
        }
    }
});

驗證 file 方法1

$('#my-form').validate({
    rules: {},
    messages: {},
    ...
});
$('input[name^="fileupload"]').rules('add', {
    required: true,
    accept: "image/jpeg, image/pjpeg"
})

驗證 file 方法2

rules: {
  'user[doc_file]': {
    required: true
  }
},
messages: {
  'user[doc_file]': {
    required: '請選擇檔案'
  }
},

messages

不管是自訂的 validation 還是預設的, 錯誤訊息 name 都是對應的

覆寫預設的錯誤訊息樣式

$.validator.setDefaults({
    errorElement: "span",
    errorClass: "help-block",
    highlight: function (element, errorClass, validClass) {
        $(element).closest('.form-group').addClass('has-error');
    },
    unhighlight: function (element, errorClass, validClass) {
        $(element).closest('.form-group').removeClass('has-error');
    },
    errorPlacement: function (error, element) {
        if (element.parent('.input-group').length || element.prop('type') === 'checkbox' || element.prop('type') === 'radio') {
            error.insertAfter(element.parent());
        } else {
            error.insertAfter(element);
        }
    }
});

判斷手機瀏覽器

function isMobile(){
    return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i.test(navigator.userAgent||navigator.vendor||window.opera)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test((navigator.userAgent||navigator.vendor||window.opera).substr(0,4)))
}

if(isMobile()) {
  alert('建議您在桌機瀏覽以獲得最佳顯示效果');
}

RWD

手機瀏覽

css 沒有直接判斷是否是手機瀏覽的函式可用, 它是判斷它的寬度, 當它寬度大於多少或小於多少, 先看範例

// This applies from 0px to 600px
body {
  background: red;
}

// This applies from 992px onwards       一般來說 bootstrap 的樣式在 992 以下就會跑掉了
@media (min-width: 992px) {
  body {
    background: green;
  }
}

有張背景圖, 如果是手機瀏覽就用比較小的那一張, 可以這樣寫 :

#banner-back-image {
    background-image:image-url('application/back_original.jpg');
    @media (max-width: 500px) {
        background-image:image-url('application/back_mobile.jpg');
    }
    background-repeat: no-repeat;
    background-size: cover;
    background-position:center center;
    padding: 50px 0; /* fix header position */
}

一般手機寬度差不多 400 左右, 所以我在這判斷小於 500 就當它是用手機瀏覽

chrome 瀏覽器上的 UI 顏色

<meta name="theme-color" content="#db5945">

斷行

.break-word {
    word-wrap: break-word;
    word-break: break-all;
}

另開新頁

唯一要注意的是如果開其他網站的話 chrome 會把它擋下來, 最好是自已網站的頁面

window.open('url...', '_blank');

fix anchor 定位偏移的問題

假設使用 bootstrap 時設定 body 出現的位置時會給 padding :

body {
     padding-top: 50px;
}

它會造成 anchor 的位置錯誤

解決方法是加上一個修正 anchor 位置的 css, 並在 anchor 上加上這個 class

.fix-anchor-position {
    padding-top: 50px;
    margin-top: -50px;
}

<div id="info" class="fix-anchor-position"> ... </div>

GA

引入 GA + [In-Page Analytics] Enable enhanced link attribution in the reports

In-Page Analytics 是用來分析網頁上按鈕各被點了幾次

1) 加上

ga('create', 'UA-XXXX-X');
ga('require', 'linkid');    <= 記得加上它
ga('send', 'pageview');

2) 設定

Admin -> Property Settings ->  Use enhanced link attribution -> Apply
Admin -> View Settings -> Website's URL   要選擇對的 protocal, 很重要, 如果網站是 https 這邊設定到 http In-Page 會一直出錯

3) 載 In-Page Analytics 的 chrome extension, 並且在 URL bar 後面有個盾, 點一下, 讓它執行 insecure code

4) 在後台 Behaviour -> In-Page Analytics 就可以看到了, 它會 iframe 你的網頁, 並且在按鈕上顯示被點擊多少次

用新版 https://www.google.com/analytics, 不要用舊版 https://analytics.google.com/analytics, 否則可能會跑不出來

[In-Page Analytics] GA 後台顯示錯誤

Problem loading In-Page Analytics
We've identified problems in your setup. These may cause problems loading In-Page Analytics.
Your site is configured to set X-Frame-Options: headers. In-Page Analytics can only work in Full View mode on your site.
You can try the Page Analytics Chrome Extension which has almost identical functionality to the In-Page Analytics report, but can often resolve these issues.

然候我看到我的網站有這個 Header

X-Frame-Options:SAMEORIGIN

browsers use this header to decide whether or not your site can be iframed by other sites.

這是因為 Rails 4 為了安全性預設加上這個 header

解決方案是把這個 header 刪除, 或 value 改成 ALLOWALL, ALLOW-FROM http://example.com

在 config/application.rb

config.action_dispatch.default_headers = {
  'X-Frame-Options' => 'ALLOW-FROM https://google.com',
}

如果 GA 的 Embedded mode 還是出不來看看開發者工具 console 有沒有噴錯

Failed to execute ‘postMessage’ on ‘DOMWindow’ (未解決)

開發者工具顯示的 error

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.google.com') does not match the recipient window's origin ('https://analytics.google.com').            inpage.js:225

有 google 到解法,手動將 analytics.google.com 改變 sub-domain 為 www.google.com 就可以了,但我會一直被 redirect 回 analytics.google.com

Track button click

它可以用來偵測特定的按扭被點選幾次

1) 先到 GA 後台設定 Event

Category : button (自已取)
Action : click (自已取)
Label : contact_translator (自已取)

HTML :

<button type="button" onclick="ga('send', 'event', 'button', 'click', 'contact_translator');"/>

當按下後,就可以馬上在 GA 後台 realtime 那裡看到結果了

其他

讓某塊這個變暗淡

.opacity-04 { opacity: 0.4; }

算出正確字數

function newline_char_count(text) {
  var newLines = text.match(/(\r\n|\n|\r)/g);
  var addition = 0;
  if (newLines != null) {
      addition = newLines.length;
  }
  return addition;
}

$('#feedback').keyup(function () {
  $('#feedback-count').html($(this).val().length + newline_char_count($(this).val()));
});

JavaScript 畫一個愛心 (用 chrome console)

s="";for(k=800;k--;)
x=1.25-k%40/16,y=k/320-1.25,
s+=Math.pow(x*x+y*y-1,3)<x*x*y*y*y
?"Love"[k%4]:39==k%40?"\n":" ";s

Comments